Skip to content

Conversation

@fortmarek
Copy link
Member

@fortmarek fortmarek commented Jan 21, 2026

Screen.Recording.2026-01-22.at.13.01.46.mov

Summary

  • Add on-demand page loading for paginated tables to support API pagination without requiring all data upfront
  • Pages are loaded via async callback when navigating, cached to avoid re-fetching
  • Shows loading indicator while fetching, error messages with retry option on failure
  • Add startPage parameter to start at a specific page
  • Add g/G keyboard shortcuts for first/last page navigation
  • Footer controls are context-aware (only show relevant shortcuts based on current page)
  • Fix 404 on Selectable table documentation page

API

try await noora.paginatedTable(
    headers: ["ID", "User", "Email", "Status"],
    pageSize: 20,
    totalPages: 50,
    startPage: 0,  // Optional: start at a specific page
    loadPage: { page in
        let response = try await api.fetchUsers(page: page, perPage: 20)
        return response.users.map { [$0.id, $0.name, $0.email, $0.status] }
    }
)

Keyboard Controls

Key Action
/ n / Space Next page
/ p Previous page
Home / g First page
End / G Last page
r Retry (on error)
q / Esc Quit

Test plan

  • Existing paginated table tests pass (backward compatibility)
  • New tests for lazy loading: page requests, caching, loading state, error handling, startPage
  • Example command: swift run examples-cli table lazyPaginated
  • Documentation updated

🤖 Generated with Claude Code

Add on-demand page loading for paginated tables to support API pagination
without requiring all data upfront.

New API:
- `paginatedTable(_:pageSize:totalPages:loadPage:)` with TableData
- `paginatedTable(headers:pageSize:totalPages:loadPage:)` with strings

Features:
- Pages are loaded on-demand when navigating
- Loaded pages are cached to avoid re-fetching
- Loading indicator shown during page fetch
- Error handling with retry (r) option
- Column widths retained during loading state to prevent visual jumps
- Non-interactive mode fallback (loads first page only)

Includes example command: `swift run examples-cli table lazyPaginated`

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@fortmarek fortmarek requested a review from a team as a code owner January 21, 2026 09:20
@fortmarek fortmarek requested review from cschmatzler and removed request for a team January 21, 2026 09:20
@dosubot dosubot bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Jan 21, 2026
@fortmarek fortmarek marked this pull request as draft January 21, 2026 09:20
fortmarek and others added 9 commits January 21, 2026 10:40
Allow starting pagination from a specific page instead of always
starting at page 0. Useful for resuming pagination or starting at
a specific point based on context.

- Add startPage parameter (0-indexed, defaults to 0)
- Clamp invalid startPage values to valid range
- Add tests for startPage functionality

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Simplify the lazy loading API by keeping only the headers: [String] variant.
This provides a cleaner, more consistent interface without the need for
users to construct TableData objects directly for lazy loading.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The renderer parameter was being ignored in the implementation,
so remove it from the protocol and all implementations.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Swift can disambiguate between sync run() and async run() based on context,
so there's no need for separate method names.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Extract common rendering and keyboard handling logic
- For static mode, pre-populate cache so it uses the same code path
- Fix inout issue by returning target page in KeyStrokeResult.loadPage
- Unified renderPage and renderFooter methods handle both modes
- Reduces code duplication significantly (~700 lines -> ~550 lines)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The public API in Noora.swift guarantees correct usage, so internal
defensive guards are not needed.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Reduces duplication by consolidating the load-render pattern into
loadAndRenderPage helper. Also unifies retry with regular page loading.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Also remove unused loadPageAsync parameter from runPaginationLoop.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add lazy loading example with async API
- Document new loadPage, totalPages, startPage parameters
- Fix navigation controls to match actual implementation
- Update interactivity to show it falls back gracefully
- Add non-interactive terminal behavior section
- Update performance considerations for lazy loading

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comment on lines 183 to 187
| `` / `Page Down` / `l` | Go to next page |
| `` / `Page Up` / `h` | Go to previous page |
| `Home` / `g` | Go to first page |
| `End` / `G` | Go to last page |
| `` / `k` | Scroll up within current page |
Copy link
Member Author

Choose a reason for hiding this comment

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

this is not something I've actually changed in this PR, it's just the documentation was not inline with the implementation.

fortmarek and others added 5 commits January 22, 2026 12:39
- g: Go to first page (same as Home)
- G: Go to last page (same as End)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Hide Home/End controls when already on first or last page since
one of them would be useless in those positions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- First page: show "End (G)" only
- Last page: show "Home (g)" only
- Middle pages: show "Home/End (g/G)"

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The navigation config links to /components/tables/selectable but
the file was named interactive.md.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comment on lines -219 to 225
| --- | --- |
| `` / `Page Down` / `l` | Go to next page |
| `` / `Page Up` / `h` | Go to previous page |
| `` / `n` / `Space` | Go to next page |
| `` / `p` | Go to previous page |
| `Home` / `g` | Go to first page |
| `End` / `G` | Go to last page |
| `` / `k` | Scroll up within current page |
| `` / `j` | Scroll down within current page |
| `r` | Retry loading (when error occurs) |
| `q` / `Esc` | Exit pagination view |
Copy link
Member Author

Choose a reason for hiding this comment

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

the keys were actually not in line with the implementation, so I'm updating the docs

@fortmarek fortmarek marked this pull request as ready for review January 22, 2026 12:08
@fortmarek fortmarek requested a review from pepicrft January 22, 2026 12:08
@dosubot dosubot bot added documentation Improvements or additions to documentation enhancement New feature or request labels Jan 22, 2026
Split the struct into extensions to reduce body length from 443 to
under 350 lines (the SwiftLint limit).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog:added documentation Improvements or additions to documentation enhancement New feature or request size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants