Skip to content

feat(media): add numbered library pagination - #2582

Open
khoinguyenpham04 wants to merge 16 commits into
mainfrom
feat/media-pagination-ui
Open

feat(media): add numbered library pagination#2582
khoinguyenpham04 wants to merge 16 commits into
mainfrom
feat/media-pagination-ui

Conversation

@khoinguyenpham04

Copy link
Copy Markdown
Collaborator

What does this PR do?

Adds numbered pagination to the local Media Library using Kumo's compound Pagination component. The new opt-in page API mode returns an exact totalCount, while existing cursor requests remain unchanged and keep their one-query path for media pickers, providers, CLI, MCP, and plugins.

The admin now provides 35/70/90 page sizes, stable loading without replacing the current grid, preserved scroll and focus, responsive wrapping, and RTL-safe controls. It also recovers cleanly when deletion or filtering makes the current page invalid, including when the result count drops to zero.

This is a follow-up to the accepted media-pagination Discussion #897 and its original cursor-based fix in #996.

Related to #897

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • Typechecks pass for the changed emdash and @emdash-cms/admin packages
  • Type-aware lint passes for the changed package scopes; whole-tree quick lint reports 0 diagnostics
  • Targeted tests pass for the API, client, Media Library, uploads, router, and cursor-based media pickers
  • All changed files pass oxfmt/Prettier checks
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable). No messages.po changes are included in the PR diff.
  • I have added and reviewed the user-facing changeset (if this PR changes a published package)
  • New features link to an approved Discussion: Media Library doesn't expose pagination — users with >50 files can't access them #897

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Codex (GPT-5)

Screenshots / test output

  • Core/API/client: 90 tests passed.
  • Admin/browser: 94 tests passed, including multi-file uploads and unchanged media-picker flows.
  • Builds: emdash, @emdash-cms/admin, and demos/simple passed.
  • Live API: page mode returned 35 items and totalCount: 127; cursor mode returned nextCursor without totalCount.
  • Live UI at 1512×982: 7 columns × 5 rows, 12px gaps, no horizontal overflow.
  • Live UI at 390×844: responsive 2-column grid and wrapped pagination with no overflow.
  • A delayed page request kept 35 items, grid height, and scroll position stable; focus returned to a valid pagination control afterward.
  • Arabic RTL: logical layout and mirrored directional controls verified.

PostgreSQL was not configured locally, so dialect tests ran on SQLite. Admin Vitest reports the repository's existing mixed vitest@4.1.5 / @vitest/browser@4.1.10 warning; all tests pass.

@changeset-bot

changeset-bot Bot commented Aug 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5e0df07

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 17 packages
Name Type
emdash Minor
@emdash-cms/admin Minor
@emdash-cms/cloudflare Minor
@emdash-cms/sandbox-workerd Patch
@emdash-cms/plugin-mcp-smoke Major
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/do-demo-site Patch
@emdash-cms/do-solo-demo-site Patch
@emdash-cms/auth Minor
@emdash-cms/blocks Minor
@emdash-cms/gutenberg-to-portable-text Minor
@emdash-cms/x402 Minor
create-emdash Minor
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

Scope check

This PR changes 912 lines across 18 files. Large PRs are harder to review and more likely to be closed without review.

If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs.

See CONTRIBUTING.md for contribution guidelines.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
docs 007ebb7 Aug 20 2026, 02:15 PM

@pkg-pr-new

pkg-pr-new Bot commented Aug 20, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@2582

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@2582

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@2582

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@2582

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@2582

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@2582

emdash

npm i https://pkg.pr.new/emdash@2582

create-emdash

npm i https://pkg.pr.new/create-emdash@2582

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@2582

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@2582

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@2582

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@2582

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@2582

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@2582

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@2582

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@2582

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@2582

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@2582

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@2582

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@2582

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@2582

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@2582

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@2582

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@2582

commit: 5e0df07

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 20, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-demo-cache 5e0df07 Aug 20 2026, 03:35 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 20, 2026

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://feat-media-pagination-ui.try.emdashcms.com, https://feat-media-pagination-ui-emdash-playground.emdash-cms.workers.dev (commit 5e0df07)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://1e9e300c.try.emdashcms.com, https://1e9e300c-emdash-playground.emdash-cms.workers.dev 5e0df07 2026-08-20T15:37:09.042Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://3e519ffc.try.emdashcms.com, https://3e519ffc-emdash-playground.emdash-cms.workers.dev aebf0c0 2026-08-20T15:19:23.096Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://64e097f4.try.emdashcms.com, https://64e097f4-emdash-playground.emdash-cms.workers.dev 007ebb7 2026-08-20T14:17:15.663Z Visit the dashboard ↗

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 20, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-demo-do 5e0df07 Aug 20 2026, 03:35 PM

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the right follow-up to Discussion #897: it adds an opt-in numbered-page mode to the media list API and a Kumo-based paginator for the local Media Library, while leaving the existing cursor path untouched for media pickers, providers, CLI, MCP, and plugins. I checked the changed files end-to-end (admin component, router state, API handler/schema/route, repository findPage, client types, and tests). SQL is parameterized, authorization still gates the route, strings go through Lingui, RTL mirroring is present, and the admin-only route does not add logged-out queries. The implementation handles page recovery when deletion/filtering empties the current page, and the totalCount is only computed in page mode.

The only blocking documentation issue is the changeset: this is a new feature that also removes hasMore/onLoadMore from the exported MediaLibraryProps, so it should be a minor bump and the entry needs more detail about the public surface and compatibility.

Comment thread .changeset/bright-media-pages.md Outdated
@github-actions github-actions Bot added review/awaiting-author Reviewed; waiting on the author to respond and removed review/needs-review No maintainer or bot review yet labels Aug 20, 2026
@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review and removed review/awaiting-author Reviewed; waiting on the author to respond labels Aug 20, 2026
@khoinguyenpham04 khoinguyenpham04 added the bot:review Trigger an emdashbot code review on this PR label Aug 20, 2026

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review of #2582: the prior blocking documentation issue is resolved. The changeset now correctly lists both affected packages ("emdash" and "@emdash-cms/admin") as minor, and it explains the user-facing capability, the opt-in page API shape, and that existing hasMore/onLoadMore props continue to work when pagination is omitted.

I re-read the full diff and traced the change end-to-end:

  • Approach: This is the right follow-up to Discussion #897. It adds an opt-in numbered-page mode to the media list API and a Kumo Pagination control for the local Media Library, while leaving the existing cursor path untouched for media pickers, providers, CLI, MCP, and plugins.
  • API & handler: mediaListQuery rejects cursor + page together and validates page to safe integers. handleMediaList caps limit at 100, validates the offset, and dispatches to repo.findPage(). The route passes totalCount through only in page mode (cursor mode omits it).
  • Repository: MediaRepository.findPage() reuses the new applyListFilters() helper so MIME/search/status filters apply consistently to both rows and count; SQL is parameterized via Kysely (escapeLike, sql tagged templates, no raw interpolation).
  • Admin UI: MediaLibrary accepts an optional pagination prop and still supports legacy hasMore/onLoadMore. Focus management after page/page-size changes is implemented with refs and an effect gated on pagination?.isPending. RTL mirroring via rtl:[&_svg]:-scale-x-100 is present.
  • Router/page state: MediaPage uses keepPreviousData to keep the current grid rendered during fetches, recovers from an invalidated higher page, and resets page to 1 on new searches/filters/page-size changes.
  • Tests: New repository, handler, schema, client, component, and router tests cover page totals, validation, empty-page recovery, focus restoration, and stable loading states. Existing cursor/picker flows remain covered.
  • Auth / logged-out queries: The media route still gates on media:read / media:upload; no new logged-out work is introduced.
  • Lingui / i18n: All new user-facing strings and ARIA labels are wrapped for translation.
  • Changeset: Now meets the .changeset/README.md standard — it leads with the released behavior, names the public surface, and includes compatibility guidance.

No further blocking issues, regressions, or convention violations remain.

@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Aug 20, 2026
@github-actions github-actions Bot added review/approved Approved; no new commits since overlap and removed review/needs-rereview Author pushed changes since the last review labels Aug 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Overlapping PRs

This PR modifies files that are also changed by other open PRs:

This may cause merge conflicts or duplicated work. A maintainer will coordinate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant