Skip to content

feat(tables): virtualize data grid with bounded copy and chunked delete#4693

Merged
TheodoreSpeaks merged 4 commits into
stagingfrom
feat/table-grid-virtualization
May 21, 2026
Merged

feat(tables): virtualize data grid with bounded copy and chunked delete#4693
TheodoreSpeaks merged 4 commits into
stagingfrom
feat/table-grid-virtualization

Conversation

@TheodoreSpeaks
Copy link
Copy Markdown
Collaborator

Summary

  • Virtualize the user-tables grid with windowed rows (@tanstack/react-virtual spacer-row approach over the native table) so 4k+ row tables scroll smoothly — DOM goes from one <tr> per loaded row to visible + overscan. Pages kept unbounded so bulk ops keep working; memory drops.
  • Copy/cut now page through all (filter-aware) rows with "Copying… / Copied N rows" toasts and a promise-based ClipboardItem (fixes clipboard gesture-expiry); capped at 50k rows with a "use Export CSV" steer beyond that. Cut clears only the copied rows.
  • Select-all → delete now covers all matching rows (not just the loaded window) and chunks deletes into ≤1000-row requests, fixing the prior >1000-row validation failure.

Type of Change

  • New feature / performance improvement

Testing

Tested manually. tsc --noEmit clean, vitest hooks/queries lib/table (202 passing), bun run lint (16/16), and check:api-validation:strict all pass.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link
Copy Markdown

vercel Bot commented May 21, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped May 21, 2026 11:00pm

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 21, 2026

PR Summary

Medium Risk
Changes core table-grid rendering and bulk clipboard/delete behaviors; virtualization plus chunked async ops can introduce subtle selection/scroll and partial-update edge cases on large datasets.

Overview
Virtualizes the table grid’s row rendering using @tanstack/react-virtual, keeping the native <table> but only rendering the visible window with spacer rows; adds header/row height measurement and updates focus scrolling to account for sticky header/gutter and windowed-out targets.

Hardens large bulk actions. Select-all delete now drains all filtered pages before prompting, and useDeleteTableRows sends row ids in MAX_BULK_OPERATION_SIZE chunks to avoid server validation limits. Copy/cut of row/column selections now pages rows via ensureRowsLoadedUpTo, writes via promise-based ClipboardItem to avoid gesture expiry, caps at TABLE_LIMITS.MAX_COPY_ROWS (50k) with user messaging, and makes cut clear only the copied rows with refetch-on-failure reconciliation.

Reviewed by Cursor Bugbot for commit 4620a07. Bugbot is set up for automated code reviews on this repo. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 21, 2026

Greptile Summary

This PR introduces three related improvements to the user-tables grid: windowed rendering via @tanstack/react-virtual (spacer-row approach over the native <table>), a promise-based ClipboardItem copy/cut implementation that pages through rows up to a 50 k cap without losing the browser gesture, and chunked sequential deletes that lift the previous 1 000-row hard limit.

  • Virtualization: sticky-header height is measured with ResizeObserver and fed as scrollMargin to the virtualizer; row height is measured from a live <td> after first load and used as the constant estimate. Scroll-to-selected-cell is updated to call scrollToIndex for off-screen targets then fine-tune with a second RAF.
  • Clipboard: ClipboardItem({ 'text/plain': blob }) is written synchronously within the copy/cut gesture; the blob is an async promise that pages data behind it. Error paths now correctly distinguish a write failure (nothing landed) from an afterCopy/clear failure (data did land).
  • Delete: useDeleteTableRows loops through ≤ 1 000-row chunks sequentially; invalidateRowCount in onSettled cascades through rowsRoot to refresh the full row list.

Confidence Score: 4/5

Safe to merge; the core virtualization, clipboard, and chunked-delete logic is sound with one minor structural defect in virtual spacer rows that does not affect functionality.

The clipboard and delete changes address real correctness problems and are well-implemented. The virtual spacer rows use colSpan={displayColumns.length + 1} but TableColGroup renders a third column group for the Add Column button, so each spacer row is one column short. With border-spacing-0 table-fixed the visual impact is nil, but the table structure is technically malformed. There are no data-loss risks or security concerns in the changed paths.

The two spacer rows in table-grid.tsx (paddingTop / paddingBottom) have the off-by-one colSpan; everything else is straightforward.

Important Files Changed

Filename Overview
apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/table-grid.tsx Adds row virtualization via @tanstack/react-virtual (spacer-row approach over native table), rewrites copy/cut to use promise-based ClipboardItem API with proper error differentiation, and fixes select-all delete to drain pages before chunked deletion. One minor colSpan off-by-one in the virtual spacer rows (missing the Add Column column).
apps/sim/app/workspace/[workspaceId]/tables/[tableId]/hooks/use-table.ts Adds ensureRowsLoadedUpTo which pages one past the cap to produce an exact hasMore signal, correctly fixing the prior off-by-one for tables with exactly MAX_COPY_ROWS rows. Cold-cache early exit is consistent with ensureAllRowsLoaded but could silently return empty on first-filter races.
apps/sim/hooks/queries/tables.ts useDeleteTableRows now chunks large delete payloads into sequential 1000-row requests. invalidateRowCount (called in onSettled) cascades through rowsRoot to invalidate the infinite row list, so UI stays consistent even on mid-chunk failures.
apps/sim/lib/table/constants.ts Adds MAX_COPY_ROWS = 50,000 constant; clean and well-documented addition.
apps/sim/package.json Adds @tanstack/react-virtual dependency for row virtualization.

Sequence Diagram

sequenceDiagram
    participant U as User (Ctrl+C/X)
    participant H as handleCopy/handleCut
    participant W as writeSelectionToClipboard
    participant CB as navigator.clipboard.write()
    participant LR as ensureRowsLoadedUpTo
    participant AC as afterCopy (clearCutRows)
    participant T as Toast

    U->>H: copy/cut gesture (transient activation)
    H->>W: call writeSelectionToClipboard(opts)
    W->>T: show Copying N rows toast
    W->>CB: write new ClipboardItem with async blob
    Note over CB: gesture activation preserved - write is synchronous
    CB-->>LR: ClipboardItem resolves blob async
    LR->>LR: page through rows up to MAX_COPY_ROWS+1
    LR-->>CB: blob resolved TSV text
    CB-->>W: writePromise resolves
    alt write failed
        W->>T: Failed to copy please try again
    else write succeeded cut
        W->>AC: afterCopy copiedRows
        AC->>AC: chunkBatchUpdates clear cells
        alt clear succeeded
            W->>T: Cut N rows or Cut first 50k rows
        else clear failed
            W->>T: Copied but clearing cells failed
        end
    else write succeeded copy
        W->>T: Copied N rows or Copied first 50k rows
    end
Loading

Reviews (2): Last reviewed commit: "fix(tables): keep copy/cut progress toas..." | Re-trigger Greptile

Comment thread apps/sim/app/workspace/[workspaceId]/tables/[tableId]/hooks/use-table.ts Outdated
…, clipboard error handling

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@TheodoreSpeaks
Copy link
Copy Markdown
Collaborator Author

@greptile review

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e9434cb. Configure here.

Comment thread apps/sim/hooks/queries/tables.ts
… error

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@TheodoreSpeaks TheodoreSpeaks merged commit 4002242 into staging May 21, 2026
14 checks passed
@TheodoreSpeaks TheodoreSpeaks deleted the feat/table-grid-virtualization branch May 21, 2026 23:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant