Repository navigation
fix(plugin-view): ObjectView fetches only for views that draw the rows, and a re-read keeps them on screen (objectui#10982) - #11049
Conversation
…s, and a re-read keeps them on screen The non-grid fetch and its data-invalidation subscription now run only for the view types whose renderer draws the fetched rows: an allow-list (kanban, calendar, gallery, timeline, map) read off each generateViewSchema case and the renderer it resolves to. The old deny-list (grid, tree, chart) let gantt, whose registered wrapper never hands the rows on, and every type with no case read and re-read rows nothing draws. A run that issues the same request as the rows on screen answer is a re-read: it keeps those rows and replaces them on arrival instead of setting loading, so a calendar handed rows no longer swaps to its loading placeholder on every bus event. The first load for a request still does. Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec Co-authored-by: Claude <noreply@anthropic.com>
…s rows on screen New pin file covers both halves: every data-drawing type is read on mount and re-read on a bus event, every self-querying or case-less type is never read by this component, a bus or onMutation re-read never paints the placeholder, a changed request and the first load still do, and a failed re-read is logged with the rows kept. The two existing pins whose stand-ins modelled gantt and tree as drawing the handed rows now model them as the real renderers behave: gantt joins the not-re-read controls, tree the self-fetching group. Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec Co-authored-by: Claude <noreply@anthropic.com>
Name the request by the inputs the query is built from, so the find call stays an inline object literal that no-unprefixed-query-params and the QueryParams excess-property check still read. Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec Co-authored-by: Claude <noreply@anthropic.com>
|
changeset-claim-re-read
|
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: Inputs read: card #10982 (body and all 3 comments: triage ① Derived judgmentsEvery accept-set and public-surface change the diff implies, each named right or wrong:
Gate verdicts on the head, read from the check-runs: ② Semver level
③ Boundary flagsDev flags (the os-dev-report's
PM note acted on (the objectui#10993 branch's three Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #10982
Clause-②: no
What this changes
ObjectViewfetches the rows for its non-grid views itself and hands them to the inner view asdata. Both halves of the card land inpackages/plugin-view/src/ObjectView.tsx, which is the producer; no other package moves.(b) The fetch runs only for view types whose renderer draws the rows
fetchDrawsViewwas a deny-list (grid,tree,chart) that gated only the data-invalidation subscription, and the fetch itself was gated ongridalone. There is now one allow-list,VIEW_TYPES_DRAWING_FETCHED_ROWS=kanban,calendar,gallery,timeline,map, declared beside the host-composition view-type record. It gates both the subscription and the fetch.The list comes from reading each
generateViewSchemacase AND the registered renderer that case resolves to. I did not copy it from the card:object-kanban,object-calendarandobject-mapspread thedataprop into their component, andobject-gallery/object-timelinereadprops.data. Each one draws the array it is handed.object-gantt's registered wrapper (ObjectGanttRenderer) passesObjectGanttthe schema and nothing else. The chart never sees the array and queries for itself. plugin-gantt already pins this (ObjectGantt.hostDataProp-7210.test.tsx), andListView'ssurfaceDrawsFetchedRowsrests on the same measurement. This is where the card's premise was incomplete. The old deny-list and the comment beside it both treatedganttas data-fed, so every ganttobject-viewwas read on mount and again on every bus event, and nothing drew those rows.object-treeruns its own full query before it looks at any rows it is handed (objectui#10778).object-chartnever reads them.page,listanddetailareViewTypemembers with nogenerateViewSchemacase. They fall through toObjectGrid, which queries for itself.Every renderer outside the list reads the bus itself. Every site that moves
refreshKeyalso notifies the bus (announceOwnWriteand theonMutationsubscription), so none of them loses its refresh.Scope, stated plainly. The card names the bus subscription. The same allow-list now also gates the mount /
refreshKeyfetch, because that read has the same defect: rows that nothing draws. The dispatch route also asked that "a new view type fetches only when its case is added to both". Measured on the base (the reverse-verification run below):treeandchartwere read once on mount, andgantt,page,listanddetailwere read on mount and again on the bus event. The tree cost more than that. With a realobject-treeunderObjectView, the base made 3 reads: one byObjectView, plus two by the tree itself, because the rows arriving changed the tree'sdatadependency and re-ran its query. At this head it makes 1, the tree's own. That was a one-time probe with both legs restored and verified by blob hash; it is not a permanent test.(c) A re-read keeps the rows on screen
The fetch used to set
loadingto true on every run. Each run now names its request: object, resolved filter, sort and expansion. The rest of thefindparams are constant. If a run's request equals the one that the rows indataanswer, it is a re-read: it leavesloadingfalse and replaces the rows when the new ones arrive. Any other run setsloadingtrue as before. That covers a first load, and also a change of filter, sort, expansion or object.loadingis set on every run, false included. So a re-read that supersedes a first load still in flight shows the rows it already has, not a placeholder the superseded run left behind.console.error,loadingends false, and the rows already drawn stay.findparams stay an inline object literal. The first draft extracted them into a variable, which would have taken this call out ofno-unprefixed-query-params(that rule reads only an inline literal) and out of theQueryParamsexcess-property check. Commit1d75b57b5put them back and keys the request on the inputs instead.Which renderers honour a
loadingprop from this component:object-calendar(only alongside an arraydata, as its registration declares) is the one that swaps rows for a placeholder.ObjectKanbanstores it but no render path reads it, and the gallery, timeline and map ignore it.Pins
ObjectView.fetchAllowList-10982.test.tsx, 16 tests, through the realSchemaRendererand registry:gantt,tree,chart,page,listanddetail: never read by this component, on mount or on a bus event, while the bare bus reader beside it moves. The inner view or the grid stand-in is asserted rendered and the schema read settled, so the zero is not vacuous.onMutationwrite never paints the placeholder.ObjectView.busReread-10887.test.tsx. Its stand-in drew the handed rows forgantt, which the real wrapper never forwards, soganttleaves the re-read list and becomes a "not re-read" control through the authored path.ObjectView.refreshInPlace-10035.test.tsx. Its stand-in modelledtreeas drawing the handed rows.treemoves to the self-fetching group and keeps the same three assertions: same instance, refetched rows, one refetch per write.Reverse verification (one-time; no permanent test)
With the pins committed at
3e312c6a8, the baseObjectView.tsx(blob08747a478) was written over the file and confirmed by blob hash. The three pin files were then run, and the file was restored withgit checkout HEAD --under a trap and verified (blob equals HEAD's,git diff HEADempty). The prediction was written down before the run: 9 red in the new file, the gantt control red inbusReread-10887, andrefreshInPlace-10035all green (it is a model correction and does not discriminate). Observed: 10 failed / 31 passed, exactly those.the gantt view was read for rows its renderer never draws: … called 2 times, and similarlytree1,chart1,page2,list2,detail2.the bus re-read swapped the rows on screen for the placeholder: expected [ { placeholder: true, rows: 1 } ] to deeply equal []{ placeholder: true, rows: 1 }paint on theonMutationre-read and on the failed re-read.ObjectView.tsxis imported by relative path in these suites, so no build sat between the mutation and the run.Gates (all read at HEAD
1d75b57b5, worktree clean)pnpm exec vitest run packages/plugin-view/: Test Files 59 passed (59), Tests 584 passed (584).ObjectView.tsxfrom disk or mounts anobject-view. The package graph cannot see those.PageView.refreshInPlace-10519(a real calendar/kanban/galleryobject-viewre-read on the bus),timelineAxisRefusalReach-7070,objectViewRouteParity.fieldOrder-10885,rowClickModifierPayloadForward-9462,ViewPreview.optionsFold-7928,ViewPreview.routeTwoGridMembers-10885,catalog-gallery-render, the fivepackages/typessource readers, and the fourscripts/__tests__readers: Test Files 16 passed (16), Tests 1211 passed (1211).pnpm --filter @object-ui/plugin-view type-check(tsc --noEmit && tsc -p tsconfig.test.json), afterturbo run build --filter='@object-ui/plugin-view^...'(15/15 tasks): exit 0.--listFilesOnlyconfirms that all three edited test files are in the test program.pnpm exec eslint --no-inline-configon the 4 changed files: 0 errors. Warnings are unchanged per file against the base (60/60, 0/0, 4/4; the new file has 0). This is a declared narrowing ofpnpm lint, backed by three pieces of evidence. Population: the config'sfiles: ['**/*.{ts,tsx}']block covers all four. Count: 4 files, read from the--format jsonoutput. Invariance:eslint.config.jssets noparserOptions.project/projectService, so linting is not type-aware and the custom rules are per-file AST rules, which means this diff cannot move a verdict on an untouched file.node scripts/check-changeset-presence.mjs✅.check:control-bytes,check:new-line-citations(0 new),check:vi-mock-specifiers,check:vi-mock-inherit,check:vi-mock-override-shape,check:test-path-roots,check:changeset-claims,check:pending-changeset-literals,check:handler-key-reads,check:i18n-keys,check:unreferenced-sources,check:phantom-depsandcheck:unused-depsall exit 0.check:changeset-claimsflagged 5 pending changesets that nameObjectView.tsx(the 6726, 7070, 7499, 8653 and object-view-unmirrored-keys-7779 bodies). Each paragraph has been read: none is falsified by this diff, because they describegenerateViewSchema, the grid node'stitleand the key census, none of which moves.Acceptance notes
loadingstarts false withdataas[], so a calendar handed rows paints an empty calendar for the window before the object schema settles, and only then the first-load placeholder. Base and head behave the same. Startingloadingtrue is not a mechanical change: anobject-viewwith no object or adapter would then sit on the placeholder forever. Out of scope here; carrier: none.@object-ui/i18nimport line, adds a top-leveluseObjectTranslationhook, and changesgetFormTitlein this file. None of those regions is touched here. objectui#10976 is queued behind this card on the same file. Whichever lands second mergesorigin/main(merge commit, no rebase) and re-runs its gates.ObjectView's comments that describedganttas drawing handed rows andtreeas re-querying on the handed array are corrected in place. They were the text the old deny-list was reasoned from.Changeset
.changeset/10982-objectview-reread.md,'@object-ui/plugin-view': patch.Implemented by the
domain:uiseat 2 dispatch, sessionhttps://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec, branchclaude/issue-10982-view-reread-allowlist.Generated by Claude Code