Skip to content

Virtualize bookmarks list with react-virtualized - #172

Merged
nwthomas merged 1 commit into
mainfrom
virtualized-bookmarks
Oct 7, 2026
Merged

nwthomas merged 1 commit into
mainfrom
virtualized-bookmarks

Conversation

@nwthomas

@nwthomas nwthomas commented Oct 7, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds react-virtualized (9.22.6, React 19 compatible) and @types/react-virtualized
  • Moves the bookmarks list into a new client component, app/bookmarks/BookmarksList.tsx, which renders it with WindowScroller + AutoSizer + List so it scrolls with the page and only keeps the visible rows (plus overscan) in the DOM
  • Row heights vary with title wrapping, so CellMeasurer measures them and the cache is cleared whenever the width changes
  • The row spacing is now padding instead of margin so the measured heights include it
  • WindowScroller reads the window size, so the virtualized list only renders after mount (via useSyncExternalStore) to avoid a hydration mismatch. The server and pre-hydration render show the first 50 bookmarks with the same markup

Notes

  • The server HTML now has only the first 50 bookmarks instead of all of them. The full list is still in /bookmarks/atom
  • The list uses role="list" / role="listitem" divs instead of ul/li, since react-virtualized renders wrapper divs

Test plan

  • eslint and tsc --noEmit pass
  • Checked in the dev server: about 37 of 334 rows in the DOM at desktop width, no overlapping rows, scrolls to the last bookmark, wrapped rows remeasure at 375px, no console errors
  • make build (not run locally)

🤖 Generated with Claude Code

@vercel

vercel Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
personal-portfolio Ready Ready Preview Oct 7, 2026 11:21pm UTC

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-07T23:24:53.084888Z 764c32a PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nwthomas
nwthomas force-pushed the virtualized-bookmarks branch from 764c32a to 8826a94 Compare October 7, 2026 23:20
@nwthomas
nwthomas merged commit 89522a6 into main Oct 7, 2026
3 checks passed
@nwthomas
nwthomas deleted the virtualized-bookmarks branch October 7, 2026 23:24

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 764c32a09e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread app/theme/route.ts Outdated
Comment on lines +55 to +58
const userId = (await getUserId()) ?? crypto.randomUUID();

try {
await setUserTheme(userId, theme);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Rate-limit anonymous user creation

A non-browser client can forge the allowed Origin header and omit cookies on every request, causing each public POST /theme call to generate a fresh UUID and insert another users row. An attacker can therefore grow the production table without bound; apply rate limiting or another server-side abuse control before creating anonymous users.

Useful? React with 👍 / 👎.

Comment thread app/hooks/useTheme.ts Outdated
Comment on lines +105 to +107
if (isTheme(serverTheme) && serverTheme !== window.__theme) {
dispatch(updateCurrentTheme(serverTheme));
window.__setPreferredTheme(serverTheme);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve a newer cookie when database persistence fails

When a user with an existing uid changes themes while the POST fails transiently, __setPreferredTheme has already updated the readable cookie but persistTheme ignores the failure. On the next load, a recovered GET returns the old database value and this block overwrites Redux, the window state, and the cookie, losing the user's selection instead of degrading to the cookie as intended. Avoid treating the database value as authoritative when a valid local choice may be newer, or track and retry failed writes.

AGENTS.md reference: AGENTS.md:L79-L82

Useful? React with 👍 / 👎.

Comment thread app/hooks/useTheme.ts Outdated
Comment on lines +65 to +67
dispatch(updateCurrentTheme(newTheme));
window.__setPreferredTheme(newTheme);
persistTheme(newTheme);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Serialize theme writes before issuing another toggle

If a new visitor toggles twice before the first POST response installs the uid cookie, these fire-and-forget calls both arrive without an identity and create different users. Response order, rather than click order, then determines the final uid and theme cookies, so a reload can show the wrong theme and an orphan row remains; queue writes or prevent another toggle until the initial identity-setting request completes.

AGENTS.md reference: AGENTS.md:L78-L81

Useful? React with 👍 / 👎.

Comment thread compose.yaml Outdated
- "${POSTGRES_PORT:-5432}:5432"
volumes:
- pgdata:/var/lib/postgresql/data
- ./db/init:/docker-entrypoint-initdb.d:ro

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Ship the users schema to the production database

When DATABASE_URL points to a fresh managed or production database, configuring the URL alone does not create the users table: the only execution path for this SQL is the local Compose initialization mount, and neither the build/start scripts nor a migration command applies it elsewhere. Consequently every theme query fails with a missing-relation error and the route returns 503 until the schema is manually installed; add a deployment migration or explicit production provisioning step.

AGENTS.md reference: AGENTS.md:L28-L28

Useful? React with 👍 / 👎.

This branch was successfully deployed

1 active deployment
Preview — 8826a94f Deployed Oct 7, 2026 by vercel[bot]
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