Skip to content

refactor(app-shell): move ObjectView's header toolbar and list URL state into sibling hooks (slice 8) - #26396

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

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

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steve · project thread

Part of #25992 (slice 8 of the move-only split; slices 1 to 7 landed as #26152, #26233, #26310, #26336, #26353 and #26374, and the card stays open for the slices listed below).

Clause-②: no
Metadata-protocol: none

What moved

Base fa5d72e1 (slice 7 merged). ui/packages/app-shell/src/views/ObjectView.tsx is 2796 lines there. Three concerns of ObjectViewInner move verbatim into custom hooks in two new sibling modules. In each, every input is passed as an argument, typed from its source (no any), the component destructures the returned values under their original names, no identifier is renamed and no context is added.

G: the header toolbar → objectViewHeaderToolbar.ts

Lines 798–874 move into useObjectViewHeaderToolbar({ activeView, localizedToolbarActions, isEnvironmentList }): bulkWiredNamesKey, headerToolbarActions (the list_toolbar actions less the record-scoped ones the selection bar carries, objectui#12462), the nav runAction deep link (useOfferedNavRunAction, toolbarActionsWithDeepLink) and the toolbarBar action:bar node. It returns headerToolbarActions, toolbarActionsWithDeepLink and toolbarBar.

The column-declaration flags right after it (lines 875–899: activeTabDeclaresColumns, draftColumnsEdited, activeViewDeclaresColumns, activeViewIsSaved) stay. Only renderListView reads them, so they go with it in slice 11.

H: the list's toolbar state in the URL → objectViewListUrlState.ts

Lines 900–1015 move into useObjectViewListUrlState({ objectName, objectDef, activeView, listPrefs, perms, user, searchParams, setSearchParams }). That covers the objectui#11860 docblock, listResetGeneration, listIdentityKey, the seed refs and the render-time seed, the write-back effect, writeListUrlState, activeViewHasPreference and handleResetView. It returns listIdentityKey, listSeedRef, ownListParamsRef, writeListUrlState, activeViewHasPreference and handleResetView. handleViewUpdate, which follows, stays: it writes the config panel's draft.

L-filter: URL equality filters and user-filter selections → objectViewListUrlState.ts

Lines 1289–1359 move into useObjectViewUrlFilters({ searchParams, setSearchParams, listIdentityKey, ownListParamsRef }), in the same module: filterParams, filterParamsKey, urlFilters (through parseUrlEqualityFilterTriples), ufSelectionsRef, initialUfSelections and handleUserFilterSelectionsChange. It returns urlFilters, initialUfSelections and handleUserFilterSelectionsChange. It takes listIdentityKey and ownListParamsRef from the list URL state hook, which is the same coupling the block had with H inside the component.

Each hook is called where its block stood, so the order of React hooks in the component is unchanged.

Imports

Each import moves with its only readers. isRecordScopedAction, actionRendersAt, useOfferedNavRunAction and bulkWiredActionNamesKey go to the header module. The whole ./userFilterUrlState.js import, readListFilterState, parseUrlEqualityFilterTriples and selectFilterParams go to the list URL module. selectFilterParams is still re-exported from ObjectView.tsx, line unchanged. Nothing new is exported from ObjectView.tsx, and the package entry is untouched. ObjectView.tsx goes from 2796 to 2554 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 five import lines or import-list entries that lost a moved name: the @object-ui/core, ./listFilterStorage.js and @object-ui/types (actionRendersAt) imports, and the selectFilterParams and bulkWiredActionNamesKey entries. The @object-ui/core and ./listFilterStorage.js lines are replaced by their trimmed forms. The added lines are the trimmed imports and the two new imports, the module headers and imports, the hook signatures and parameter types, the return objects, and in ObjectView.tsx the three calls with their destructuring and two-line comments.

Test edits

One, path only, as the seat allowed:

  1. ObjectView.urlFilterSuffix-9196.test.ts, describe "objectui#9196 — ObjectView no longer keeps a private reader of this family": readFileSync(path.join(here, 'ObjectView.tsx'), 'utf8') → readFileSync(path.join(here, 'objectViewListUrlState.ts'), 'utf8'). The block it scans (const urlFilters, parseUrlEqualityFilterTriples, the ./drillUrlFilters.js import, no filter\[ regex literal) now lives there. No assertion changed.

No other source reader of ObjectView.tsx names a moved line.

Seam table rows this PR changes (measured on fa5d72e1)

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

# Concern Lines on fa5d72e1 Reads Read by Slice
G Header toolbar actions, nav runAction deep link, action:bar node 798–874 D (localizedToolbarActions, isEnvironmentList), F (activeView) P, Q 8, this PR
G-columns Column-declaration flags of the active view 875–899 B (viewDraft), F O 11, with O
H List toolbar state in the URL: seed, write-back, Reset view 900–1015 A, C (objectDef, perms, user), F (activeView, listPrefs) L-filter, O, P, Q 8, this PR
L-filter filter[...] equality filters, uf_* user-filter selections 1289–1359 A, H (listIdentityKey, ownListParamsRef) O 8, this PR

Remaining slices (the last closes the card)

  • Slice 9 — N + P: the view tab bar and the plugin schema plus header chrome.
  • Slice 10 — Q: the JSX return's PageHeader, import wizard and NavigationOverlay blocks into sub-components with explicit props.
  • Slice 11 — B + I + O + G-columns (closes the card): the view-config and view-tab CRUD handlers, renderListView and the column flags only it reads. The writeRefusalCensus-11583 rows and the relayRungCensus-7559 read are re-pointed (path and file-key changes only). check-i18n-call-site-keys.test.ts also reads ObjectView.tsx for the deleteViewConfirm call site in handleDeleteView, and is re-pointed the same way.

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/, the AppContent* tests, every other test that reads ObjectView.tsx as source, the app-shell tests that touch the list URL state, list filter storage, list preferences, drillUrlFilters or the nav runAction, eslint on the 4 touched files, the derived dispatch gates, and a Console build.

Acceptance notes

  • eslint: 0 errors and no new no-explicit-any. The hook boundary adds three warnings that the same code did not raise inside one component, with dependency arrays kept verbatim because the slice is move-only:
    • react-hooks/exhaustive-deps on handleUserFilterSelectionsChange: ownListParamsRef now arrives as a parameter, and the rule only treats a useRef as stable inside the hook that declares it. It is the same ref object, so leaving it out of the deps changes nothing.
    • react-hooks/refs twice, on the return of useObjectViewUrlFilters (at urlFilters and initialUfSelections). initialUfSelections is read from ufSelectionsRef during render, as it was in the component, and the rule flags the values a hook returns from a render that reads a ref.
      The four files report 132 warnings, against 129 for the base ObjectView.tsx alone.

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


🤖 Generated with Claude Code

https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz

claude added 18 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>
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Oct 27, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

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

Head 96a92cad (read from the PR object). It merges main abb90460, which is after #26367 and after slice 7 (#26374).

  • Shape. Draft on main, Part of #25992, slice 8. 5 files, +413/-281. Three concerns move verbatim. Each hook is called where its block stood, so the hook order is unchanged:
    • G (header toolbar actions, the nav runAction deep link, toolbarBar) → useObjectViewHeaderToolbar in objectViewHeaderToolbar.ts.
    • H (the list's uf_ URL state: seed, write-back, Reset view) → useObjectViewListUrlState in objectViewListUrlState.ts.
    • L-filter → useObjectViewUrlFilters in the same module. It takes listIdentityKey and ownListParamsRef from the H hook.
    • G-columns (read only by renderListView) and handleViewUpdate stay.
      ObjectView.tsx goes from 2796 to 2554 lines. One path-only test edit: urlFilterSuffix-9196 now reads objectViewListUrlState.ts, as planned in the seam table. Root changeset. Clause-②: no, Metadata-protocol: none.
  • 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 26396: not governed.
  • Seat review.
    • Move-only: the multiset proof holds, and the only base lines missing are 5 trimmed imports. Parameters are typed from their sources, with no any.
    • eslint: 0 errors and no new no-explicit-any. The hook boundary adds 3 warnings (132 against 129 at base): one exhaustive-deps naming ownListParamsRef, now a parameter, and two react-hooks/refs on useObjectViewUrlFilters' return. The deps are kept verbatim. The seat takes the warnings over folding H and L-filter into one hook, which would move L-filter's hooks about 280 lines earlier in the order and break move-only.
  • Evidence (dev).
    • Type-check passes. 197 files / 3335 tests. Console build.
    • After the main merge: closure build, type-check, 272 tests and the Console build are green again.
    • 36/36 gates, 0 NOT-MEASURED.
  • Serial constraints. No other open PR touches ObjectView.tsx or its objectView* siblings. app-shell has no sdui record. 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 02:26
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 27, 2026 02:26
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit e677770 Oct 27, 2026
54 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-x004t9 branch October 27, 2026 02:46
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/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants