From a23dd20e6ba419c9ac21d9e55a5f3eb446f0fee8 Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Mon, 30 Mar 2026 18:23:47 -0700 Subject: [PATCH 1/2] fetch total count in the background, not foreground --- src/commands/blueprint/list.tsx | 36 +++++++++++-------- src/commands/devbox/list.tsx | 36 +++++++++++-------- src/commands/gateway-config/list.tsx | 36 +++++++++++-------- src/commands/mcp-config/list.tsx | 36 +++++++++++-------- src/commands/network-policy/list.tsx | 36 +++++++++++-------- src/commands/object/list.tsx | 36 +++++++++++-------- src/commands/secret/list.tsx | 36 +++++++++++-------- src/commands/snapshot/list.tsx | 36 +++++++++++-------- src/hooks/useCursorPagination.ts | 54 +++++++++++++++++++--------- 9 files changed, 213 insertions(+), 129 deletions(-) diff --git a/src/commands/blueprint/list.tsx b/src/commands/blueprint/list.tsx index 68c39847..39b0a531 100644 --- a/src/commands/blueprint/list.tsx +++ b/src/commands/blueprint/list.tsx @@ -364,9 +364,13 @@ const ListBlueprintsUI = ({ // Calculate pagination info for display const totalPages = Math.max(1, Math.ceil(totalCount / PAGE_SIZE)); const startIndex = currentPage * PAGE_SIZE; - const endIndex = Math.min(startIndex + blueprints.length, totalCount); - const showingRange = - endIndex === startIndex + 1 + const endIndex = + totalCount > 0 + ? Math.min(startIndex + blueprints.length, totalCount) + : startIndex + blueprints.length; + const showingRange = navigating + ? `${startIndex + 1}+` + : endIndex === startIndex + 1 ? `${startIndex + 1}` : `${startIndex + 1}-${endIndex}`; @@ -908,14 +912,18 @@ const ListBlueprintsUI = ({ {/* Statistics Bar */} {!showPopup && ( - - {figures.hamburger} {totalCount} - - - {" "} - total - - {totalPages > 1 && ( + {totalCount > 0 && ( + <> + + {figures.hamburger} {totalCount} + + + {" "} + total + + + )} + {totalCount > 0 && totalPages > 1 && ( <> {" "} @@ -933,11 +941,11 @@ const ListBlueprintsUI = ({ )} - {" "} - •{" "} + {totalCount > 0 ? " • " : ""} - Showing {showingRange} of {totalCount} + Showing {showingRange} + {totalCount > 0 ? ` of ${totalCount}` : ""} {search.submittedSearchQuery && ( <> diff --git a/src/commands/devbox/list.tsx b/src/commands/devbox/list.tsx index 4bc1f2ee..c0057967 100644 --- a/src/commands/devbox/list.tsx +++ b/src/commands/devbox/list.tsx @@ -425,9 +425,13 @@ const ListDevboxesUI = ({ // Calculate pagination info for display const totalPages = Math.max(1, Math.ceil(totalCount / PAGE_SIZE)); const startIndex = currentPage * PAGE_SIZE; - const endIndex = Math.min(startIndex + devboxes.length, totalCount); - const showingRange = - endIndex === startIndex + 1 + const endIndex = + totalCount > 0 + ? Math.min(startIndex + devboxes.length, totalCount) + : startIndex + devboxes.length; + const showingRange = navigating + ? `${startIndex + 1}+` + : endIndex === startIndex + 1 ? `${startIndex + 1}` : `${startIndex + 1}-${endIndex}`; @@ -732,14 +736,18 @@ const ListDevboxesUI = ({ {/* Statistics Bar - hide when popup is shown */} {!showPopup && ( - - {figures.hamburger} {totalCount} - - - {" "} - total - - {totalPages > 1 && ( + {totalCount > 0 && ( + <> + + {figures.hamburger} {totalCount} + + + {" "} + total + + + )} + {totalCount > 0 && totalPages > 1 && ( <> {" "} @@ -757,11 +765,11 @@ const ListDevboxesUI = ({ )} - {" "} - •{" "} + {totalCount > 0 ? " • " : ""} - Showing {showingRange} of {totalCount} + Showing {showingRange} + {totalCount > 0 ? ` of ${totalCount}` : ""} {search.submittedSearchQuery && ( <> diff --git a/src/commands/gateway-config/list.tsx b/src/commands/gateway-config/list.tsx index 7529127a..7f118eda 100644 --- a/src/commands/gateway-config/list.tsx +++ b/src/commands/gateway-config/list.tsx @@ -312,9 +312,13 @@ const ListGatewayConfigsUI = ({ // Calculate pagination info for display const totalPages = Math.max(1, Math.ceil(totalCount / PAGE_SIZE)); const startIndex = currentPage * PAGE_SIZE; - const endIndex = Math.min(startIndex + configs.length, totalCount); - const showingRange = - endIndex === startIndex + 1 + const endIndex = + totalCount > 0 + ? Math.min(startIndex + configs.length, totalCount) + : startIndex + configs.length; + const showingRange = navigating + ? `${startIndex + 1}+` + : endIndex === startIndex + 1 ? `${startIndex + 1}` : `${startIndex + 1}-${endIndex}`; @@ -679,14 +683,18 @@ const ListGatewayConfigsUI = ({ {/* Statistics Bar - hide when popup is shown */} {!showPopup && ( - - {figures.hamburger} {totalCount} - - - {" "} - total - - {totalPages > 1 && ( + {totalCount > 0 && ( + <> + + {figures.hamburger} {totalCount} + + + {" "} + total + + + )} + {totalCount > 0 && totalPages > 1 && ( <> {" "} @@ -704,11 +712,11 @@ const ListGatewayConfigsUI = ({ )} - {" "} - •{" "} + {totalCount > 0 ? " • " : ""} - Showing {showingRange} of {totalCount} + Showing {showingRange} + {totalCount > 0 ? ` of ${totalCount}` : ""} {search.submittedSearchQuery && ( <> diff --git a/src/commands/mcp-config/list.tsx b/src/commands/mcp-config/list.tsx index b25895a7..33be6789 100644 --- a/src/commands/mcp-config/list.tsx +++ b/src/commands/mcp-config/list.tsx @@ -274,9 +274,13 @@ const ListMcpConfigsUI = ({ const totalPages = Math.max(1, Math.ceil(totalCount / PAGE_SIZE)); const startIndex = currentPage * PAGE_SIZE; - const endIndex = Math.min(startIndex + configs.length, totalCount); - const showingRange = - endIndex === startIndex + 1 + const endIndex = + totalCount > 0 + ? Math.min(startIndex + configs.length, totalCount) + : startIndex + configs.length; + const showingRange = navigating + ? `${startIndex + 1}+` + : endIndex === startIndex + 1 ? `${startIndex + 1}` : `${startIndex + 1}-${endIndex}`; @@ -604,14 +608,18 @@ const ListMcpConfigsUI = ({ {!showPopup && ( - - {figures.hamburger} {totalCount} - - - {" "} - total - - {totalPages > 1 && ( + {totalCount > 0 && ( + <> + + {figures.hamburger} {totalCount} + + + {" "} + total + + + )} + {totalCount > 0 && totalPages > 1 && ( <> {" "} @@ -629,11 +637,11 @@ const ListMcpConfigsUI = ({ )} - {" "} - •{" "} + {totalCount > 0 ? " • " : ""} - Showing {showingRange} of {totalCount} + Showing {showingRange} + {totalCount > 0 ? ` of ${totalCount}` : ""} {search.submittedSearchQuery && ( <> diff --git a/src/commands/network-policy/list.tsx b/src/commands/network-policy/list.tsx index 9e6d9bdf..9a212284 100644 --- a/src/commands/network-policy/list.tsx +++ b/src/commands/network-policy/list.tsx @@ -349,9 +349,13 @@ const ListNetworkPoliciesUI = ({ // Calculate pagination info for display const totalPages = Math.max(1, Math.ceil(totalCount / PAGE_SIZE)); const startIndex = currentPage * PAGE_SIZE; - const endIndex = Math.min(startIndex + policies.length, totalCount); - const showingRange = - endIndex === startIndex + 1 + const endIndex = + totalCount > 0 + ? Math.min(startIndex + policies.length, totalCount) + : startIndex + policies.length; + const showingRange = navigating + ? `${startIndex + 1}+` + : endIndex === startIndex + 1 ? `${startIndex + 1}` : `${startIndex + 1}-${endIndex}`; @@ -708,14 +712,18 @@ const ListNetworkPoliciesUI = ({ {/* Statistics Bar - hide when popup is shown */} {!showPopup && ( - - {figures.hamburger} {totalCount} - - - {" "} - total - - {totalPages > 1 && ( + {totalCount > 0 && ( + <> + + {figures.hamburger} {totalCount} + + + {" "} + total + + + )} + {totalCount > 0 && totalPages > 1 && ( <> {" "} @@ -733,11 +741,11 @@ const ListNetworkPoliciesUI = ({ )} - {" "} - •{" "} + {totalCount > 0 ? " • " : ""} - Showing {showingRange} of {totalCount} + Showing {showingRange} + {totalCount > 0 ? ` of ${totalCount}` : ""} {search.submittedSearchQuery && ( <> diff --git a/src/commands/object/list.tsx b/src/commands/object/list.tsx index 6dd47980..ae006afa 100644 --- a/src/commands/object/list.tsx +++ b/src/commands/object/list.tsx @@ -351,9 +351,13 @@ const ListObjectsUI = ({ // Calculate pagination info for display const totalPages = Math.max(1, Math.ceil(totalCount / PAGE_SIZE)); const startIndex = currentPage * PAGE_SIZE; - const endIndex = Math.min(startIndex + objects.length, totalCount); - const showingRange = - endIndex === startIndex + 1 + const endIndex = + totalCount > 0 + ? Math.min(startIndex + objects.length, totalCount) + : startIndex + objects.length; + const showingRange = navigating + ? `${startIndex + 1}+` + : endIndex === startIndex + 1 ? `${startIndex + 1}` : `${startIndex + 1}-${endIndex}`; @@ -749,14 +753,18 @@ const ListObjectsUI = ({ {/* Statistics Bar - hide when popup is shown */} {!showPopup && ( - - {figures.hamburger} {totalCount} - - - {" "} - total - - {totalPages > 1 && ( + {totalCount > 0 && ( + <> + + {figures.hamburger} {totalCount} + + + {" "} + total + + + )} + {totalCount > 0 && totalPages > 1 && ( <> {" "} @@ -774,11 +782,11 @@ const ListObjectsUI = ({ )} - {" "} - •{" "} + {totalCount > 0 ? " • " : ""} - Showing {showingRange} of {totalCount} + Showing {showingRange} + {totalCount > 0 ? ` of ${totalCount}` : ""} {search.submittedSearchQuery && ( <> diff --git a/src/commands/secret/list.tsx b/src/commands/secret/list.tsx index 7602d137..80b5aab0 100644 --- a/src/commands/secret/list.tsx +++ b/src/commands/secret/list.tsx @@ -241,9 +241,13 @@ const ListSecretsUI = ({ // Calculate pagination info for display const totalPages = Math.max(1, Math.ceil(totalCount / PAGE_SIZE)); const startIndex = currentPage * PAGE_SIZE; - const endIndex = Math.min(startIndex + secrets.length, totalCount); - const showingRange = - endIndex === startIndex + 1 + const endIndex = + totalCount > 0 + ? Math.min(startIndex + secrets.length, totalCount) + : startIndex + secrets.length; + const showingRange = navigating + ? `${startIndex + 1}+` + : endIndex === startIndex + 1 ? `${startIndex + 1}` : `${startIndex + 1}-${endIndex}`; @@ -550,14 +554,18 @@ const ListSecretsUI = ({ {/* Statistics Bar - hide when popup is shown */} {!showPopup && ( - - {figures.hamburger} {totalCount} - - - {" "} - total - - {totalPages > 1 && ( + {totalCount > 0 && ( + <> + + {figures.hamburger} {totalCount} + + + {" "} + total + + + )} + {totalCount > 0 && totalPages > 1 && ( <> {" "} @@ -575,11 +583,11 @@ const ListSecretsUI = ({ )} - {" "} - •{" "} + {totalCount > 0 ? " • " : ""} - Showing {showingRange} of {totalCount} + Showing {showingRange} + {totalCount > 0 ? ` of ${totalCount}` : ""} {search.submittedSearchQuery && ( <> diff --git a/src/commands/snapshot/list.tsx b/src/commands/snapshot/list.tsx index 7cdba0f1..0577b8d2 100644 --- a/src/commands/snapshot/list.tsx +++ b/src/commands/snapshot/list.tsx @@ -276,9 +276,13 @@ const ListSnapshotsUI = ({ // Calculate pagination info for display const totalPages = Math.max(1, Math.ceil(totalCount / PAGE_SIZE)); const startIndex = currentPage * PAGE_SIZE; - const endIndex = Math.min(startIndex + snapshots.length, totalCount); - const showingRange = - endIndex === startIndex + 1 + const endIndex = + totalCount > 0 + ? Math.min(startIndex + snapshots.length, totalCount) + : startIndex + snapshots.length; + const showingRange = navigating + ? `${startIndex + 1}+` + : endIndex === startIndex + 1 ? `${startIndex + 1}` : `${startIndex + 1}-${endIndex}`; @@ -621,14 +625,18 @@ const ListSnapshotsUI = ({ {/* Statistics Bar - hide when popup is shown */} {!showPopup && ( - - {figures.hamburger} {totalCount} - - - {" "} - total - - {totalPages > 1 && ( + {totalCount > 0 && ( + <> + + {figures.hamburger} {totalCount} + + + {" "} + total + + + )} + {totalCount > 0 && totalPages > 1 && ( <> {" "} @@ -646,11 +654,11 @@ const ListSnapshotsUI = ({ )} - {" "} - •{" "} + {totalCount > 0 ? " • " : ""} - Showing {showingRange} of {totalCount} + Showing {showingRange} + {totalCount > 0 ? ` of ${totalCount}` : ""} {search.submittedSearchQuery && ( <> diff --git a/src/hooks/useCursorPagination.ts b/src/hooks/useCursorPagination.ts index b695596a..52528686 100644 --- a/src/hooks/useCursorPagination.ts +++ b/src/hooks/useCursorPagination.ts @@ -108,6 +108,9 @@ export function useCursorPagination( // Track if we have a cached total count from the API (to avoid re-requesting) const hasCachedTotalCountRef = React.useRef(false); + // Abort controller for cancelling in-flight count requests + const countAbortRef = React.useRef(null); + // Cursor history: cursorHistory[N] = last item ID of page N // Used to determine startingAt for page N+1 const cursorHistoryRef = React.useRef<(string | undefined)[]>([]); @@ -176,13 +179,11 @@ export function useCursorPagination( const startingAt = page > 0 ? cursorHistoryRef.current[page - 1] : undefined; - // Only request total_count on first fetch or when we don't have it cached - const includeTotalCount = !hasCachedTotalCountRef.current; - + // Never request total_count in the main data fetch — it's fetched asynchronously const result = await fetchPageRef.current({ limit: pageSizeRef.current, startingAt, - includeTotalCount, + includeTotalCount: false, }); if (!isMountedRef.current) return; @@ -201,18 +202,12 @@ export function useCursorPagination( // Update pagination state setHasMore(result.hasMore); - // Use API's totalCount if available (only on first fetch), otherwise keep existing - if ( - result.totalCount !== undefined && - result.totalCount > 0 && - !hasCachedTotalCountRef.current - ) { - setTotalCount(result.totalCount); + // If has_more is false on any page, we know the exact total count. + // Cancel the background count request if still pending. + if (!result.hasMore && !hasCachedTotalCountRef.current) { + countAbortRef.current?.abort(); + setTotalCount(page * pageSizeRef.current + result.items.length); hasCachedTotalCountRef.current = true; - } else if (!hasCachedTotalCountRef.current) { - // Fallback: compute from items seen so far (for APIs that don't support total_count) - const computed = page * pageSizeRef.current + result.items.length; - setTotalCount((prev) => Math.max(computed, prev)); } } catch (err) { if (!isMountedRef.current) return; @@ -237,9 +232,34 @@ export function useCursorPagination( setCurrentPage(0); setItems([]); setHasMore(false); - setTotalCount(0); - // Fetch page 0 + // Don't reset totalCount to 0 — keep old value visible while new count loads + // Fire both data and count requests immediately in parallel. + // If the data request returns first with hasMore=false, cancel the count request. + countAbortRef.current?.abort(); + const countAbort = new AbortController(); + countAbortRef.current = countAbort; + let cancelled = false; + + // Data fetch fetchPageData(0, true); + + // Background count fetch — fires immediately alongside data + fetchPageRef + .current({ limit: 0, startingAt: undefined, includeTotalCount: true }) + .then((result) => { + if (cancelled || countAbort.signal.aborted || !isMountedRef.current) + return; + if (result.totalCount !== undefined) { + setTotalCount(result.totalCount); + hasCachedTotalCountRef.current = true; + } + }) + .catch(() => {}); // count failure is non-critical + + return () => { + cancelled = true; + countAbort.abort(); + }; }, [depsKey, fetchPageData]); // Polling effect - STOP polling when there's an error to avoid flickering From d361751e3a1b9b73a9e0b1c07f28192b9a246e87 Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Thu, 2 Apr 2026 15:19:23 -0700 Subject: [PATCH 2/2] fix: hide "Showing" range text when filtered results are empty When there are 0 items (e.g. empty search results or no resources), the stats bar incorrectly showed "Showing 1-0". Now the "Showing" text is hidden entirely when there are no items to display. Co-Authored-By: Claude Opus 4.6 --- src/commands/blueprint/list.tsx | 18 +++++++++++------- src/commands/devbox/list.tsx | 22 +++++++++++++++------- src/commands/gateway-config/list.tsx | 18 +++++++++++------- src/commands/mcp-config/list.tsx | 18 +++++++++++------- src/commands/network-policy/list.tsx | 18 +++++++++++------- src/commands/object/list.tsx | 18 +++++++++++------- src/commands/secret/list.tsx | 18 +++++++++++------- src/commands/snapshot/list.tsx | 18 +++++++++++------- 8 files changed, 92 insertions(+), 56 deletions(-) diff --git a/src/commands/blueprint/list.tsx b/src/commands/blueprint/list.tsx index 39b0a531..746f2a5b 100644 --- a/src/commands/blueprint/list.tsx +++ b/src/commands/blueprint/list.tsx @@ -940,13 +940,17 @@ const ListBlueprintsUI = ({ )} )} - - {totalCount > 0 ? " • " : ""} - - - Showing {showingRange} - {totalCount > 0 ? ` of ${totalCount}` : ""} - + {endIndex > startIndex && ( + <> + + {totalCount > 0 ? " • " : ""} + + + Showing {showingRange} + {totalCount > 0 ? ` of ${totalCount}` : ""} + + + )} {search.submittedSearchQuery && ( <> diff --git a/src/commands/devbox/list.tsx b/src/commands/devbox/list.tsx index c0057967..20de58b5 100644 --- a/src/commands/devbox/list.tsx +++ b/src/commands/devbox/list.tsx @@ -764,13 +764,21 @@ const ListDevboxesUI = ({ )} )} - - {totalCount > 0 ? " • " : ""} - - - Showing {showingRange} - {totalCount > 0 ? ` of ${totalCount}` : ""} - + {devboxes.length > 0 && ( + <> + {endIndex > startIndex && ( + <> + + {totalCount > 0 ? " • " : ""} + + + Showing {showingRange} + {totalCount > 0 ? ` of ${totalCount}` : ""} + + + )} + + )} {search.submittedSearchQuery && ( <> diff --git a/src/commands/gateway-config/list.tsx b/src/commands/gateway-config/list.tsx index 7f118eda..09650469 100644 --- a/src/commands/gateway-config/list.tsx +++ b/src/commands/gateway-config/list.tsx @@ -711,13 +711,17 @@ const ListGatewayConfigsUI = ({ )} )} - - {totalCount > 0 ? " • " : ""} - - - Showing {showingRange} - {totalCount > 0 ? ` of ${totalCount}` : ""} - + {endIndex > startIndex && ( + <> + + {totalCount > 0 ? " • " : ""} + + + Showing {showingRange} + {totalCount > 0 ? ` of ${totalCount}` : ""} + + + )} {search.submittedSearchQuery && ( <> diff --git a/src/commands/mcp-config/list.tsx b/src/commands/mcp-config/list.tsx index 33be6789..4fe13af7 100644 --- a/src/commands/mcp-config/list.tsx +++ b/src/commands/mcp-config/list.tsx @@ -636,13 +636,17 @@ const ListMcpConfigsUI = ({ )} )} - - {totalCount > 0 ? " • " : ""} - - - Showing {showingRange} - {totalCount > 0 ? ` of ${totalCount}` : ""} - + {endIndex > startIndex && ( + <> + + {totalCount > 0 ? " • " : ""} + + + Showing {showingRange} + {totalCount > 0 ? ` of ${totalCount}` : ""} + + + )} {search.submittedSearchQuery && ( <> diff --git a/src/commands/network-policy/list.tsx b/src/commands/network-policy/list.tsx index 9a212284..d0e3f572 100644 --- a/src/commands/network-policy/list.tsx +++ b/src/commands/network-policy/list.tsx @@ -740,13 +740,17 @@ const ListNetworkPoliciesUI = ({ )} )} - - {totalCount > 0 ? " • " : ""} - - - Showing {showingRange} - {totalCount > 0 ? ` of ${totalCount}` : ""} - + {endIndex > startIndex && ( + <> + + {totalCount > 0 ? " • " : ""} + + + Showing {showingRange} + {totalCount > 0 ? ` of ${totalCount}` : ""} + + + )} {search.submittedSearchQuery && ( <> diff --git a/src/commands/object/list.tsx b/src/commands/object/list.tsx index ae006afa..1bffd700 100644 --- a/src/commands/object/list.tsx +++ b/src/commands/object/list.tsx @@ -781,13 +781,17 @@ const ListObjectsUI = ({ )} )} - - {totalCount > 0 ? " • " : ""} - - - Showing {showingRange} - {totalCount > 0 ? ` of ${totalCount}` : ""} - + {endIndex > startIndex && ( + <> + + {totalCount > 0 ? " • " : ""} + + + Showing {showingRange} + {totalCount > 0 ? ` of ${totalCount}` : ""} + + + )} {search.submittedSearchQuery && ( <> diff --git a/src/commands/secret/list.tsx b/src/commands/secret/list.tsx index 80b5aab0..58e7e56b 100644 --- a/src/commands/secret/list.tsx +++ b/src/commands/secret/list.tsx @@ -582,13 +582,17 @@ const ListSecretsUI = ({ )} )} - - {totalCount > 0 ? " • " : ""} - - - Showing {showingRange} - {totalCount > 0 ? ` of ${totalCount}` : ""} - + {endIndex > startIndex && ( + <> + + {totalCount > 0 ? " • " : ""} + + + Showing {showingRange} + {totalCount > 0 ? ` of ${totalCount}` : ""} + + + )} {search.submittedSearchQuery && ( <> diff --git a/src/commands/snapshot/list.tsx b/src/commands/snapshot/list.tsx index 0577b8d2..153f2888 100644 --- a/src/commands/snapshot/list.tsx +++ b/src/commands/snapshot/list.tsx @@ -653,13 +653,17 @@ const ListSnapshotsUI = ({ )} )} - - {totalCount > 0 ? " • " : ""} - - - Showing {showingRange} - {totalCount > 0 ? ` of ${totalCount}` : ""} - + {endIndex > startIndex && ( + <> + + {totalCount > 0 ? " • " : ""} + + + Showing {showingRange} + {totalCount > 0 ? ` of ${totalCount}` : ""} + + + )} {search.submittedSearchQuery && ( <>