Repository navigation
fix(plugin-list,plugin-calendar): a list's calendar view fetches the days it shows, walked in fetch batches (objectui#12081 item 2) - #12116
Conversation
…it draws, walked in fetch batches A calendar under a list view drew the rows of ListView's one unpaged fetch: the first 100 records of the object, with no date condition. The fetch now selects the calendar's visible days on its start (and end) field, plus the records with no start date, and walks them in steps of the fetch batch up to the non-grid ceiling, whose footnote ListView draws. ObjectCalendar reports the days it draws through a new onVisibleRangeChange host callback. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AswpQDLCKiZos2jCXknwKz
…s date window, its walk, its ceiling and the calendar's range report Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AswpQDLCKiZos2jCXknwKz
…wn module; docs, changeset and the 9853 calendar row The grid's week arithmetic moves to visibleDays.ts so the calendar's range report survives a test that stubs the CalendarView module. ListView's calendar branch keeps its own binding reads. The 9853 pin's calendar row now asserts the walk: every step is the fetch batch. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AswpQDLCKiZos2jCXknwKz
…etState in an effect, for the calendar window The calendar refreshes its callback ref in an effect. ListView holds the window and its truncation in one state and drops it during render when the view leaves the calendar. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AswpQDLCKiZos2jCXknwKz
✅ 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: ① Derived judgmentsInputs: the card (objectui#12081, body and every comment through the Gate verdicts on the head: 40 check-runs
② Semver levelChangeset ③ Boundary flags
Deviations (five), each answered:
Out-of-scope findings (three, carrier none), each answered:
Also noted by this review: the 9853 pin's header sentence named in ① item 5. Implemented-by: VERDICT: PASS Generated by Claude Code |
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AswpQDLCKiZos2jCXknwKz
…es a windowed calendar's fetch, said where it was said otherwise The fetch-batch docblock and the 9853 pin's header said a declared pagination.pageSize sizes every unpaged view's fetch; a calendar with a start date bound now walks its window in batch steps whatever is declared. The changeset names that and the withheld rows-per-page picker. The calendar docs' "Date Filtering" bullet described a standalone behaviour that does not exist; it now points at the hosted path. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AswpQDLCKiZos2jCXknwKz
✅ 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: ① Derived judgmentsInputs: the card (objectui#12081, body and every comment through the patch-round What moved since record Gate verdicts on this head, read at 2026-10-11T04:11Z: 42 check-runs, 31
② Semver levelChangeset ③ Boundary flags
The REWORK Deviations from the first report (five), each answered:
Deviations from the patch round (two), each answered:
Out-of-scope findings from the first report (three, carrier none), each answered:
Noted by this review, carrier none: the renderer docblock's "eight callbacks" count (① item 2) and the short-batch stop's reliance on a server that answers Implemented-by: VERDICT: PASS Generated by Claude Code |
Part of #12081 (item 2)
Clause-②: yes
A calendar view inside a list now draws every record of the days it shows. Before, it drew only the first 100 records of the object. Items 3, 4 and 7 of the card stay open, and #12081 is not addressed beyond item 2 here.
What was measured on
main(5330afd)ListViewmakes one unpaged fetch for every view except the flat grid. Its$topiseffectivePageSize, which falls back toDEFAULT_LIST_FETCH_BATCH_SIZE(100), and it carries no date condition. The rows are handed to theobject-calendarnode asdata. The renderer forwards an arraydata, andObjectCalendarthen skips its own fetch. That fetch is the one that carries the ruling a′ ceiling (2,001 with the probe row) and its footnote. So the hotclm readingtop=100&select=…,end_dateis this fetch, and its only sign was the list's "Showing first 100 records" note.main. New pinListView.calendarDateWindow-12081, case (a), with only the test file present:AssertionError: expected [ 'sep-0', 'sep-1', 'sep-2', …(97) ] to include 'oct-60'. The calendar was handed the first 100 rows of the object, mostly from the month before.ObjectCalendarstate (currentDate,view), whichCalendarViewmirrors. The month grid is 42 days from the display locale's first day of the week, and on a phone the view switches to day by itself.ListViewreceived none of it: it passed neitheronNavigatenoronViewChange, and neither one fires for the state the calendar opens on.What changed
ListView(calendarWindowed). With a start binding, the calendar view's fetch selects the visible days onstartDateField. With anendDateFieldbound, it selects every span that touches them. It also selects the records with no start date, soObjectCalendar's Unscheduled area keeps its records. The window node joins the effective filter under oneand, so the view filter, panel, user filters and search still narrow it. The bounds are localYYYY-MM-DDdates, widened by one day on each side, so a UTC-stored instant always lands inside the window.fetchCalendarWindowin the newplugin-list/src/calendarWindow.ts). Every request has$topat most 100, with$skipadvancing. The walk stops at the first short batch, or one probe row pastNON_GRID_ROW_CEILING.applyNonGridRowCeilingthen cuts the result, andListViewdrawsNonGridRowCeilingNoteunder the calendar naming N and M. ⛔ No bigger cap.dataLimitReachedstays off for a windowed calendar. The rows-per-page picker is withheld there, because a page size changes nothing the calendar draws.ObjectCalendarComponentProps.onVisibleRangeChange({ start, end }). It fires on mount and whenever navigation or a view change moves the range. The range is half-open over local midnights. It is computed bygetVisibleDateRange, built on the samegetMonthDaysandgetWeekStartthe grids draw with. Those two helpers moved verbatim fromCalendarView.tsxinto the newplugin-calendar/src/visibleDays.ts, so the report also survives the tests that stub theCalendarViewmodule. The effect is keyed on the two instants, and the callback is read through a ref, so an unchanged range is never reported twice. Theobject-calendarrenderer forwards it through its declared host-callback list, as it doesonNavigate.initialCalendarWindow). So the first fetch does not wait for the calendar, and a host-registered calendar that never reports still draws today's month. The calendar's first report falls inside that window, so it asks for nothing more. A range the held window covers (a week of the same month, or the phone's day view) asks for nothing; one it does not cover fetches that window. A windowed calendar is not swapped for the list's loading skeleton, so it keeps the month it moved to across the refetch. It keeps drawing while the next window loads, under the existing refresh bar. Leaving the calendar view drops its window. This is done during render, so a calendar shown again opens on today's window.ObjectCalendar's query has no search channel. It would also have broken the toolbar Refresh, the client-side export and the user-filter counts, all of which read this view's rows, and each would have needed another prop. So the fetch stays withListView. The standaloneObjectCalendarfetch (no hostdata) is unchanged.ListView.tsxandObjectCalendar.tsxwith their tests, plus the changeset. Named extensions: two new pure modules beside them (calendarWindow.ts,visibleDays.ts), the renderer's host-callback list inplugin-calendar/src/index.tsx, thecalendarrow ofListView.pageSizeAndFetchBatch-9853(it now asserts the walk: every step is the batch, never the page size), the cross-package pin inapp-shell(the only package depending on both), and the docs that AGENTS.md's docs-driven rule asks for (plugin-listREADME's paging paragraph, which this change made false;plugin-calendarREADME;content/docs/plugins/plugin-calendar.mdx).ListView's calendar branch keeps its own reads of the two bindings, which thecalendarUnionReads-8651source pin reads. The window reads the same two expressions at component scope, with a comment tying the pair together.Clause-② (yes): what the published surface gains
ObjectCalendarComponentProps.onVisibleRangeChange?: (range: { start: Date; end: Date }) => void, optional.plugin-listpackage entry is not in the diff. Inplugin-calendar's entry, only theHOST_CALLBACKSarray changes. The new modules are not re-exported, and noexport *exists in either entry.common.rowCeilingNote/common.rowCeilingNoteUnknownTotal.@object-ui/typeschanges, and the view schema is unchanged.Tests (head
25e515d)ListView.calendarDateWindow-12081(11 cases). (a): 122 October records with 40 August records listed ahead are all handed over; every step has$topat most 100; the walk takes more than one step; the filter names the field the calendar draws by; no out-of-grid month is handed over; nodata-limit-warningappears. (b) twice: an uncovered range refetches December whole, and a covered range sends zero requests. (c): 2,050 records hand 2,000 rows, the walk asks for 2,001 in total, and therole=notefootnote names 2000 and 2050. Also: a fitting window draws no note; undated records ride along; a span opening before the window is fetched; the view filter still narrows. CONTROLS: the paged grid's requests are a literal equal to before ($top= the spec display default, then$skipon page 2, no window); the board is still one batch with no filter; a calendar with no start binding keeps its single batch and gets no callback.ObjectCalendar.visibleRange-12081(6 cases): the month grid underen-USanden-GB, Next period, the week view, the day view, and no re-report on a fresh handler.app-shelllistCalendarDateWindow-12081: the REALListViewover the REALObjectCalendar. All 122 October chips are drawn. Next period draws all 110 November chips, and the header still reads November 2026.25e515d. The four product files were restored to5330afd, with each blob verified on disk. The predicted direction was red. Observed:Tests 15 failed | 14 passed (29)across the four pin files. The 14 passing are the CONTROL rows, the 9853 rows of the other views, the filter case, and the no-re-report case, which passes vacuously onmain. The restore wasgit checkout HEAD -- …, with every blob equal to HEAD andgit diff HEAD0 bytes.pnpm exec vitest run packages/plugin-list/ packages/plugin-calendar/ …listCalendarDateWindow-12081.test.tsxgaveTest Files 188 passed (188),Tests 1837 passed | 95 skipped. That run was atda0bab9.25e515dchanges only the window-state plumbing inListView.tsxand the ref write inObjectCalendar.tsx. After it, the 6 directly affected files are green (56 passed). The union re-run at25e515dis in progress, and its verdict lands in theos-dev-reportcomment on console (17.7.0): seven UI defects measured in one full browser pass of a metadata app — missing Edit, capped calendar, page-only footer sum, ignored password, and three affordances shown to users who cannot use them #12081.Gates (head
25e515d)pnpm --filter @object-ui/plugin-list type-checkandpnpm --filter @object-ui/plugin-calendar type-check: both scripts echoedtsc --noEmit && tsc -p tsconfig.test.json, exit 0.check:new-line-citations(VERDICT 0 new citations),check:control-bytes,check-changeset-presence(10 source files of 3 released packages, 1 changeset),check-changeset-no-major,check:changeset-claims,check:pending-changeset-literals,check:test-path-roots,check:vi-mock-specifiers,check:vi-mock-inherit,check:vi-mock-override-shape,check:esm-specifiers(specifiers only),check:self-import,check:phantom-deps,check:handler-key-reads,check:spec-symbols,check:doc-fences,check:doc-example-ids. A raw control-byte self-scan of the changed files found 0.check:readme-exports. Reason: it needs every package built (population floor 25), and this tree built only the closure. The README edits add no fenced block or import.check:eager-closure. It needs a base and a head console build. The reading lands in the report.eslint.config.js's**/*.{ts,tsx}blocks; the 10 touched.ts/.tsxfiles all fall in them. ②--format jsonread 10 files, 0 errors. ③ Invariance: the config has noprojectService,parserOptions.projectorTypeCheckedpreset, so linting is not type-aware and this diff cannot move any untouched file's verdict. On added lines, the only non-anywarnings sit on the two pre-existing dependency-array lines. Both exist onmain: the fetch effect's are suppressed by its inline directive undereslint ., and the view memo's (onColumnStateChange) is a standing warning. Repo-widepnpm lintis declared to CI.Acceptance notes (observed, not filed)
ObjectCalendar(no hostdata) still fetches its whole filtered set up to the a′ ceiling, with its footnote, rather than its visible window. It is loud, not silent, so it is not a defect. It could reusegetVisibleDateRange. Carrier: none.$skiprelies on the backend answering the view's$orderby, or no order, stably between requests. The paged grid's pager relies on the same property. Carrier: none.b5a5d1b:content/docs/plugins/plugin-calendar.mdxused to list "Date Filtering: Automatically filters records by date range" as anObjectCalendarfeature. The bullet now names the visible-day report, links the Visible days section, and says that the standalone component fetches its whole filtered set up to the row ceiling.25e515d. The landing headb5a5d1badds only amainmerge and prose: theDEFAULT_LIST_FETCH_BATCH_SIZEdocblock, the 9853 pin's header, two changeset bullets and the mdx bullet. No code or assertion changed. Contract review6105341715is onb5a5d1b.Generated by Claude Code