Skip to content

feat(storage): paginate remote storage listings with a "Load more" button#9834

Merged
Light2Dark merged 6 commits into
mainfrom
Light2Dark/feat-storage-pagination
Jun 15, 2026
Merged

feat(storage): paginate remote storage listings with a "Load more" button#9834
Light2Dark merged 6 commits into
mainfrom
Light2Dark/feat-storage-pagination

Conversation

@Light2Dark

@Light2Dark Light2Dark commented Jun 9, 2026

Copy link
Copy Markdown
Member

📝 Summary

Split out from #9708. Part of the work for #9662.

Adds pagination to remote storage listings:

  • Threads a page_token through the storage backends, the StorageListEntriesCommand, and the StorageEntriesNotification. list_entries now returns a StorageListResult (entries + next_page_token + may_have_more).
  • Surfaces a "Load more" button in the storage inspector that fetches and appends the next page, plus a "More files may exist" hint when a provider truncates a listing it cannot page through.
Screen.Recording.2026-06-10.at.11.56.00.AM.mov

📋 Pre-Review Checklist

  • For large changes, or changes that affect the public API: this change was discussed or approved through an issue, on Discord, or the community discussions (Please provide a link if applicable).
  • Any AI generated code has been reviewed line-by-line by the human PR author, who stands by it.
  • Video or media evidence is provided for any visual changes (optional).

✅ Merge Checklist

  • I have read the contributor guidelines.
  • Documentation has been updated where applicable, including docstrings for API changes.
  • Tests have been added for the changes made.

@vercel

vercel Bot commented Jun 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment Jun 15, 2026 2:59pm

Request Review

@cubic-dev-ai cubic-dev-ai 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.

2 issues found across 15 files

Architecture diagram
sequenceDiagram
    participant User
    participant UI as Storage Inspector (React)
    participant State as Jotai State (Atom/Reducer)
    participant Backend as Runtime Commands
    participant Storage as Storage Backend (Obstore/Fsspec)
    participant External as Object Store (S3/GCS/...)

    Note over UI,External: Paginated storage listing flow (NEW)

    UI->>State: useStorageEntries(namespace, prefix)
    State-->>UI: cached entries & pageMetadata (if any)
    alt Cache miss (first fetch)
        UI->>Backend: ListStorageEntries (pageToken: null)
        Backend->>Storage: list_entries(limit=150, page_token=None)
        Storage->>External: list entries (e.g., list_with_delimiter)
        External-->>Storage: raw entries (may be provider-truncated)
        alt Obstore: result length >= 1000
            Storage->>Storage: set may_have_more=true (provider limit)
        end
        Storage-->>Backend: StorageListResult(entries, next_page_token, may_have_more)
        Backend-->>UI: StorageEntriesNotification(entries, next_page_token, may_have_more)
        UI->>State: setEntries({entries, nextPageToken, mayHaveMore, append: false})
        State-->>UI: entriesByPath + pageMetadataByPath updated
    end
    UI->>UI: Render entries + "Load more" button (if next_page_token != null)
    UI->>UI: Render "More files may exist" hint (if may_have_more && !next_page_token)

    User->>UI: Click "Load more"
    UI->>UI: loadMore() – guard: skip if isLoadingMoreRef or no token
    UI->>Backend: ListStorageEntries (pageToken: next_page_token)
    Backend->>Storage: list_entries(limit=150, page_token)
    Storage->>Storage: slice entries[offset:offset+limit]
    Storage-->>Backend: StorageListResult(new_entries, new_next_page_token, may_have_more)
    Backend-->>UI: StorageEntriesNotification(new_entries, new_next_page_token, ...)
    UI->>State: setEntries({append: true, entries: new_entries, nextPageToken, mayHaveMore})
    State-->>UI: entries appended, pageMetadata updated
    UI->>UI: Re-render button (hidden if new_next_page_token null)
    UI->>UI: Show "More files may exist" if applicable

    opt Error during loadMore
        UI->>UI: setLoadMoreError(error)
        UI->>UI: Display error message next to button
    end
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread frontend/src/core/storage/state.ts Outdated
Comment thread frontend/src/components/storage/storage-inspector.tsx Outdated
Base automatically changed from Light2Dark/fix-storage-duplicate-paths to main June 9, 2026 16:28
@Light2Dark
Light2Dark force-pushed the Light2Dark/feat-storage-pagination branch from fe2773c to e446002 Compare June 9, 2026 17:35
@Light2Dark Light2Dark added the enhancement New feature or request label Jun 9, 2026

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 3 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread frontend/src/components/storage/storage-inspector.tsx Outdated
@Light2Dark
Light2Dark marked this pull request as ready for review June 10, 2026 04:00
Copilot AI review requested due to automatic review settings June 10, 2026 04:00

Copilot AI 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.

Pull request overview

Adds end-to-end pagination support for remote storage listings, enabling the frontend storage inspector to fetch additional pages of entries via a “Load more” interaction while also signaling when a provider may have truncated results that cannot be paged further.

Changes:

  • Introduces StorageListResult (entries + next_page_token + may_have_more) and threads page_token through backend implementations, runtime command handling, and notifications.
  • Updates the frontend storage state/hook layer to track pagination metadata per namespace/prefix and append subsequent pages into the cached listing.
  • Adds UI affordances in the storage inspector for “Load more” and a “More files may exist” hint, with corresponding test updates.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/_runtime/test_runtime_external_storage.py Updates runtime storage listing tests to exercise page_token and next_page_token.
tests/_data/_external_storage/test_storage_models.py Updates backend model tests for StorageListResult and adds pagination/may-have-more coverage.
packages/openapi/src/api.ts Extends generated OpenAPI TS types for new pagination fields.
packages/openapi/api.yaml Extends OpenAPI schema for pageToken request field and pagination fields on notifications.
marimo/_server/models/models.py Threads page_token from HTTP request model into the runtime command.
marimo/_runtime/commands.py Adds page_token to StorageListEntriesCommand.
marimo/_runtime/callbacks/external_storage.py Calls backend list_entries(..., page_token=...) and includes pagination metadata in StorageEntriesNotification.
marimo/_messaging/notification.py Adds next_page_token and may_have_more to StorageEntriesNotification.
marimo/_data/_external_storage/storage.py Changes storage backends to return StorageListResult and implements page slicing + “may have more” detection for obstore listings.
marimo/_data/_external_storage/models.py Introduces StorageListResult and updates the StorageBackend.list_entries contract accordingly.
frontend/src/core/storage/types.ts Extends storage state types with per-path pagination metadata.
frontend/src/core/storage/state.ts Implements page fetching/append logic and exposes loadMore and pagination state from useStorageEntries.
frontend/src/core/storage/tests/useStorageEntries.test.tsx Adds coverage for load-more behavior, empty-string page tokens, and duplicate load-more suppression.
frontend/src/core/storage/tests/state.test.ts Adds reducer/state coverage for pagination metadata + append behavior.
frontend/src/components/storage/storage-inspector.tsx Adds “Load more” button and “More files may exist” hint UI using new hook outputs.

@Light2Dark
Light2Dark requested review from dmadisetti, kirangadhave and mscolnick and removed request for dmadisetti June 11, 2026 04:41

@kirangadhave kirangadhave left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

looks great, some nits and questions

Comment thread frontend/src/components/storage/storage-inspector.tsx Outdated
Comment thread frontend/src/core/storage/state.ts
Comment thread frontend/src/core/storage/state.ts Outdated
Comment thread marimo/_data/_external_storage/storage.py Outdated
@codecov

codecov Bot commented Jun 12, 2026

Copy link
Copy Markdown

Bundle Report

Changes will increase total bundle size by 11.9kB (0.05%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
marimo-esm 25.33MB 11.9kB (0.05%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: marimo-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
assets/JsonOutput-*.js 10.49kB 571.76kB 1.87%
assets/index-*.js 1.81kB 433.0kB 0.42%
assets/index-*.css 118 bytes 368.19kB 0.03%
assets/dist-*.js 283 bytes 387 bytes 272.12% ⚠️
assets/dist-*.js -220 bytes 183 bytes -54.59%
assets/dist-*.js 73 bytes 256 bytes 39.89% ⚠️
assets/dist-*.js -157 bytes 102 bytes -60.62%
assets/dist-*.js 23 bytes 160 bytes 16.79% ⚠️
assets/dist-*.js -65 bytes 104 bytes -38.46%
assets/dist-*.js 73 bytes 177 bytes 70.19% ⚠️
assets/dist-*.js 99 bytes 276 bytes 55.93% ⚠️
assets/dist-*.js -12 bytes 164 bytes -6.82%
assets/dist-*.js -283 bytes 104 bytes -73.13%
assets/dist-*.js -27 bytes 137 bytes -16.46%
assets/dist-*.js 33 bytes 137 bytes 31.73% ⚠️
assets/dist-*.js -172 bytes 104 bytes -62.32%
assets/dist-*.js 175 bytes 335 bytes 109.38% ⚠️
assets/dist-*.js 220 bytes 403 bytes 120.22% ⚠️
assets/dist-*.js 39 bytes 176 bytes 28.47% ⚠️
assets/dist-*.js 157 bytes 259 bytes 153.92% ⚠️
assets/dist-*.js -73 bytes 183 bytes -28.52%
assets/dist-*.js -166 bytes 169 bytes -49.55%
assets/layout-*.js 8 bytes 203.22kB 0.0%
assets/dependency-*.js -10 bytes 156.74kB -0.01%
assets/file-*.js 1 bytes 102.68kB 0.0%
assets/file-*.js 1.16kB 48.31kB 2.45%
assets/input-*.js 38 bytes 60.64kB 0.06%
assets/session-*.js -27 bytes 26.08kB -0.1%
assets/react-*.browser.esm-CV8-hvjx.js (New) 25.64kB 25.64kB 100.0% 🚀
assets/vega-*.browser-C8wT63Va.js (New) 24.8kB 24.8kB 100.0% 🚀
assets/state-*.js 580 bytes 2.85kB 25.49% ⚠️
assets/home-*.js 8 bytes 24.18kB 0.03%
assets/command-*.js 8 bytes 18.34kB 0.04%
assets/react-*.esm-BNzu6e7h.js (New) 8.37kB 8.37kB 100.0% 🚀
assets/column-*.js -38 bytes 6.49kB -0.58%
assets/ttcn-*.js -7 bytes 57 bytes -10.94%
assets/ttcn-*.js 7 bytes 64 bytes 12.28% ⚠️
assets/emotion-*.esm-C59xfSYt.js (New) 4.37kB 4.37kB 100.0% 🚀
assets/tree-*.js 552 bytes 3.09kB 21.72% ⚠️
assets/components-*.js (Deleted) -2.58kB 0 bytes -100.0% 🗑️
assets/mermaid-*.core-B73Gp-Wo.js (New) 2.38kB 2.38kB 100.0% 🚀
assets/numbers-*.js 268 bytes 1.63kB 19.62% ⚠️
assets/errors-*.js 171 bytes 1.1kB 18.43% ⚠️
assets/eye-*.js 182 bytes 612 bytes 42.33% ⚠️
assets/ellipsis-*.js (Deleted) -235 bytes 0 bytes -100.0% 🗑️
assets/__vite-*.js -5 bytes 93 bytes -5.1%
assets/__vite-*.js 5 bytes 98 bytes 5.38% ⚠️
assets/react-*.browser.esm-BdtIs0E-.js (Deleted) -25.64kB 0 bytes -100.0% 🗑️
assets/vega-*.browser-xq8miGHn.js (Deleted) -24.8kB 0 bytes -100.0% 🗑️
assets/react-*.esm--O4lBTlZ.js (Deleted) -8.37kB 0 bytes -100.0% 🗑️
assets/emotion-*.esm-Dangy3Bv.js (Deleted) -4.37kB 0 bytes -100.0% 🗑️
assets/mermaid-*.core-Cw_lBEej.js (Deleted) -2.38kB 0 bytes -100.0% 🗑️
assets/maps-*.js (Deleted) -595 bytes 0 bytes -100.0% 🗑️

Files in assets/file-*.js:

  • ./src/components/storage/storage-inspector.tsx → Total Size: 24.96kB

Files in assets/state-*.js:

  • ./src/core/storage/state.ts → Total Size: 4.38kB

  • ./src/core/storage/types.ts → Total Size: 434 bytes

Light2Dark and others added 4 commits June 15, 2026 11:32
…tton

Thread page tokens through the storage backends, runtime command, and
notification, and surface a "Load more" control (plus a "may have more"
hint when a provider truncates a listing) in the storage inspector.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… more" tooltip

- loadMore now guards on `nextPageToken == null` instead of falsy, so an
  empty-string token (treated as a valid page by hasMore and the page
  fetcher) is no longer silently dropped.
- Make the "More files may exist" info tooltip keyboard-focusable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The previous label duplicated the adjacent visible text; give the
focusable icon a concise accessible name instead, leaving the detail to
the tooltip description.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread packages/openapi/src/api.ts Outdated
/** @default null */
error?: string | null;
/** @default false */
may_have_more?: boolean;

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.

what about just has_more?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I've removed this field entirely. It's very rarely to hit, (user needs to scroll to 1000 items within a directory) + click "loads more" a few times.

Collapse pagination to a single concept: next_page_token. The
may_have_more flag was a non-actionable "more files may exist" hint for
the rare >1000-entries-per-directory case (which obstore directory
listing can't page past), and reviewers found the dual signal confusing.
Properly supporting >1000 entries needs a larger listing redesign and is
deferred.

Removes the flag and the LIMIT_ENTRIES boundary check end-to-end:
backend result/notification, runtime callback, frontend state/hook, the
"More files may exist" UI, regenerated OpenAPI, and the related tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai 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.

2 issues found across 12 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="frontend/src/core/storage/state.ts">

<violation number="1" location="frontend/src/core/storage/state.ts:155">
P2: Dropping `may_have_more` here prevents the inspector from surfacing truncated listings that cannot be paged through.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread marimo/_data/_external_storage/storage.py
Comment thread frontend/src/core/storage/state.ts
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Light2Dark
Light2Dark merged commit efd920d into main Jun 15, 2026
48 checks passed
@Light2Dark
Light2Dark deleted the Light2Dark/feat-storage-pagination branch June 15, 2026 15:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bash-focus Area to focus on during release bug bash enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants