Skip to content

Fix blocklist filtering from page state - #145

Merged
AlejandroAkbal merged 3 commits into
mainfrom
dependency-update-adoption
Jun 23, 2026
Merged

AlejandroAkbal merged 3 commits into
mainfrom
dependency-update-adoption

Conversation

@AlejandroAkbal

@AlejandroAkbal AlejandroAkbal commented Jun 22, 2026 •

Copy link
Copy Markdown
Member

Summary

  • keep TanStack infinite-query pages raw and apply active blocklist filtering in page-level computed rows
  • share blocklist post matching across posts and saved-posts pages
  • update blocklist toast copy now that matching posts hide automatically
  • add browser regressions for loaded posts, reload stability, and saved-post filtering

Tests

  • corepack pnpm vitest run test/pages/posts-tag-collections.test.ts test/pages/premium-cloud.test.ts
  • corepack pnpm typecheck
  • corepack pnpm exec prettier --check AGENTS.md app/assets/js/post-blocklist.ts 'app/pages/posts/[domain]/index.vue' 'app/pages/premium/saved-posts/[domain].vue' test/pages/posts-tag-collections.test.ts test/pages/premium-cloud.test.ts i18n/locales/en.json i18n/locales/es.json i18n/locales/id.json i18n/locales/fr.json i18n/locales/ko.json i18n/locales/ja.json i18n/locales/it.json i18n/locales/vi.json i18n/locales/zh.json i18n/locales/pt.json i18n/locales/tr.json i18n/locales/de.json i18n/locales/ru.json
  • corepack pnpm exec eslint app/assets/js/post-blocklist.ts 'app/pages/posts/[domain]/index.vue' 'app/pages/premium/saved-posts/[domain].vue' test/pages/posts-tag-collections.test.ts test/pages/premium-cloud.test.ts

Summary by CodeRabbit

  • New Features

    • Posts matching blocklist tags are now automatically hidden without requiring a page reload.
    • Blocklist filtering applies to both regular posts and saved posts.
  • Bug Fixes

    • Blocklisted/non-renderable content is now excluded during list virtualization rendering, rather than earlier query shaping.
  • Localization

    • Updated blocklist notification messages across multiple languages to say matching posts are hidden automatically.
  • Tests

    • Added/updated Playwright tests to verify filtering and “no results” behavior for custom blocklists.
  • Documentation

    • Clarified blocklist filtering behavior as client-side UI state.

@coderabbitai

coderabbitai Bot commented Jun 22, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3ca177b4-9d17-4e2d-a46f-6eb20b595fbc

📥 Commits

Reviewing files that changed from the base of the PR and between 8af24c5 and 0ac3f5a.

📒 Files selected for processing (3)
  • app/pages/posts/[domain]/index.vue
  • test/pages/posts.test.ts
  • test/server-mocks/plugin.ts
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Nuxt 4 with Vue 3 and TypeScript for the application stack

Files:

  • test/pages/posts.test.ts
  • test/server-mocks/plugin.ts
  • app/pages/posts/[domain]/index.vue
**/*.{js,ts,tsx,vue,css,json}

📄 CodeRabbit inference engine (AGENTS.md)

Format code with Prettier using 120-char print width, no semicolons, single quotes, trailing commas removed, single attribute per line in Vue templates

Files:

  • test/pages/posts.test.ts
  • test/server-mocks/plugin.ts
  • app/pages/posts/[domain]/index.vue
**/*.{js,ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,tsx,vue}: Use Nuxt flat ESLint configuration via @nuxt/eslint for linting
For URL validation/parsing, prefer URL.canParse() or URL.parse() over constructor try/catch; use URL.parse() when the parsed URL object is needed

Files:

  • test/pages/posts.test.ts
  • test/server-mocks/plugin.ts
  • app/pages/posts/[domain]/index.vue
**/*.{vue,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Use useLazyToast() to lazy-load vue-sonner and render ClientToaster on first use; wait for ClientToaster to mount before calling toast.* (plain nextTick() can fire before mounting and drop the toast)

Files:

  • test/pages/posts.test.ts
  • test/server-mocks/plugin.ts
  • app/pages/posts/[domain]/index.vue
test/**/*.{test,spec}.{js,ts}

📄 CodeRabbit inference engine (AGENTS.md)

test/**/*.{test,spec}.{js,ts}: Use @nuxt/test-utils with Playwright inside describe blocks calling await setup({ browser: true }); mock server API calls via test-only Nitro plugin at test/server-mocks/plugin.ts
Use fetch from @nuxt/test-utils with { redirect: 'manual' } to capture redirect status and Location headers (no .raw on $fetch wrapper); reference test/server/redirect-removed-locales.test.ts
Import localeCodes, prefixedLocaleCodes, and removedLocaleCodes from config/i18n in locale-related tests instead of hardcoding locale lists
Plain Vitest suites that import app modules directly do not get Nuxt's runtime alias resolution; keep repository/pure modules importable through relative paths or import from app path

Files:

  • test/pages/posts.test.ts
**/*.{ts,vue}

📄 CodeRabbit inference engine (AGENTS.md)

Use PocketBase batch writes for multi-record replacement/reorder operations instead of one HTTP write per changed row

Files:

  • test/pages/posts.test.ts
  • test/server-mocks/plugin.ts
  • app/pages/posts/[domain]/index.vue
app/pages/**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

Use useSeoMeta for page-specific tags (title, description) in page components; schema.org breadcrumb URLs should remain local/locale-relative and not be converted to project.urls.production

Files:

  • app/pages/posts/[domain]/index.vue
**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

**/*.vue: Use @nuxt/image v2 module API for image preload priority with preload: { fetchPriority: 'high' } instead of patching rendered HTML in Nitro
Use Lazy* components, dynamic imports, or similarly deferred boundaries for interaction-gated loading of features only needed after user opens a menu/sheet/dialog
Keep @formkit/auto-animate route-scoped via local vAutoAnimate imports on premium CSR pages instead of registering globally to reduce first-load entry size
For state immediately persisted with VueUse moveArrayElement(), build the reordered array synchronously instead of reading it before VueUse applies the move on nextTick

Files:

  • app/pages/posts/[domain]/index.vue
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: Track locale analytics via Matomo (app/plugins/040.matomo.client.ts tracking to.fullPath) separately from GSC Performance organic search data; do not conflate usage metrics when deciding locale retention
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: Use GSC Performance filters with URLs containing prefix (e.g., `/ru/` without trailing wildcard) and cross-check with Country → Pages; avoid URL params like `page=*%2Fru%2F*` which report false zeros
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: Expand the locale list only with GSC evidence of organic demand; retire locale prefixes with negligible prefixed-URL GSC traffic via removedLocaleCodes and permanent 301 redirects
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: Prefer Nitro/server for URL, SEO, and redirect behavior; use server-only permanent redirects via Nitro middleware with sendRedirect(..., 301) instead of duplicating redirects in app/middleware/*.global.ts
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: Do not add client middleware for parity when the server already handles the case; follow the retired-locale redirect pattern (server/middleware/redirect-removed-locales.ts) as the reference for server-only redirects
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: Use flat bracket keys (filter[sort], filter[rating], etc.) for query filters with Vue Router's default query handling; do not re-add `qs` for nested objects unless the URL contract changes
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: Route Gelbooru media through Cloudflare Worker media proxies before imgproxy; use clean upstream headers in Worker fetches without forwarding CF-*, X-Forwarded-*, X-Real-IP, cookies, or authorization headers
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: When a premium prompt is triggered from inside a bottom sheet or dialog, let the sheet/dialog owner close the local UI, open the premium prompt, and restore the local UI after the prompt closes
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: Default to Nitro middleware, server plugins, and SSR head tags over app/middleware and client-only solutions; reach for app/middleware only when SPA navigation genuinely requires client-side routing behavior
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: After substantial performance changes, verify with a production build, relevant tests, request traces, and Lighthouse against the built app before deciding to keep the change
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: Do not add app-level HTML compression; production is behind Cloudflare which Brotli-compresses HTML responses
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: Keep the global TanStack Vue Query plugin (queryClient) in the entry point unless a larger measured payoff appears; route-scoped QueryClient experiments showed minimal compression savings without Lighthouse improvements
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: Do not add service worker logic; the service worker is intentionally disabled (selfDestroying: true)
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: Source map uploads happen only in production Docker builds with SENTRY_ORG, SENTRY_PROJECT, SENTRY_AUTH_TOKEN; set SENTRY_UPLOAD_SOURCE_MAPS=false to skip
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: For premium and PocketBase flows, use a real authenticated browser session for final investigation when possible; unit tests prove payload/repository behavior but real-browser traces catch request bursts and auth redirects
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: Empty cloud state means no user-authored cloud override; do not seed PocketBase from local defaults during initial load; only write premium cloud records after explicit user edits
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: PocketBase realtime subscriptions echo local writes; inspect real network traces and separate write requests from realtime-triggered refreshes when debugging sync performance
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: Saved posts use the same premium cloud realtime runtime as tag collections, custom boorus, and custom blocklist; empty saved-post cloud state means no saved posts
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: Premium auth transitions are reload-backed in the dashboard/sign-in flow, so premium sync state does not need per-user owner scoping in useState; rely on page reload to clear memory state
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: Docker production build: multi-stage with pnpm install --frozen-lockfile in build stage; set SENTRY_UPLOAD_SOURCE_MAPS=false to skip source map uploads if build args not provided
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: Docker production build: NITRO_PRESET build arg selects the deployment target; production stage copies only .output/ (no node_modules needed)
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-23T06:39:20.653Z
Learning: Do not carry local dependency patches (pnpm patch, patch-package, patchedDependencies) in the repo; prefer upstream releases, supported configurations, or ignoring non-user-facing Sentry noise
📚 Learning: 2026-06-22T07:50:54.935Z
Learnt from: AlejandroAkbal
Repo: Rule-34/App PR: 144
File: test/server/fix-canonical-queries.test.ts:174-178
Timestamp: 2026-06-22T07:50:54.935Z
Learning: In this Nuxt 3/4 project using `nuxt-schema-org` (v6.2.1), breadcrumb `source`/`itemListElement` URL values may be provided via `localePath()` (locale-relative in source code), but `nuxt-schema-org` renders JSON-LD with production-absolute URLs using `site.url` during schema rendering. When asserting JSON-LD breadcrumb `itemListElement` URLs in tests, expect production-absolute URLs (e.g., prefix with `project.urls.production.origin`), not the locale-relative `localePath()` forms. Do not flag production-absolute URL strings in JSON-LD breadcrumb tests as incorrect.

Applied to files:

  • test/pages/posts.test.ts
🔇 Additional comments (7)
app/pages/posts/[domain]/index.vue (4)

10-10: LGTM!


575-576: LGTM!


586-602: LGTM!


913-913: LGTM!

test/server-mocks/plugin.ts (2)

10-10: LGTM!


76-79: LGTM!

test/pages/posts.test.ts (1)

388-402: LGTM!


📝 Walkthrough

Walkthrough

Blocklist filtering is moved from TanStack infinite-query select transformations into page-level allRows computed properties for both the posts page and the saved-posts page. A new postHasBlockedTag utility centralizes the per-post tag intersection check. Toast strings across all locales are updated to reflect that posts are now hidden automatically. Tests verify post filtering behavior and non-renderable post handling.

Changes

Client-side blocklist filtering refactor

Layer / File(s) Summary
postHasBlockedTag utility and convention docs
app/assets/js/post-blocklist.ts, AGENTS.md
Adds postHasBlockedTag(post, blockedTags) that checks all post tag categories against a blocked set, short-circuiting when the set is empty. Documents the convention that blocklist filtering must be applied at the page-level row-building step, not in query select.
Posts page: remove query select filter, apply at allRows
app/pages/posts/[domain]/index.vue
Removes the useInfiniteQuery select option that previously filtered posts. Introduces a blockedTags computed Set from selectedBlockList, calls postHasBlockedTag in the allRows pipeline to exclude blocked posts, and updates first-page schema filtering.
Saved-posts page: add blocklist filtering to allRows
app/pages/premium/saved-posts/[domain].vue
Imports postHasBlockedTag, reads selectedBlockList from useBlockLists(), derives a blockedTags Set, and updates the allRows pipeline to exclude posts where postHasBlockedTag returns true.
Toast copy updated across all locales
i18n/locales/*.json
Updates toasts.tagAddedToBlocklistDescription in all 13 locale files from reload/re-search instructions to a statement that matching posts are hidden automatically.
E2E and integration tests for blocklist filtering and non-renderable post handling
test/pages/posts-tag-collections.test.ts, test/pages/premium-cloud.test.ts, test/pages/posts.test.ts, test/server-mocks/plugin.ts
Updates the posts tag-collections test to verify a post disappears after its tag is blocklisted and remains hidden after reload, with no Node.insertBefore errors. Adds a premium cloud test asserting the saved-posts page renders zero posts when the active custom blocklist matches fetched post tags. Adds a posts test for non-renderable post handling with unknown media types. Extends the server mock plugin to support the unknown-media test scenario.

Possibly Related PRs

  • Rule-34/App#124: Modifies the same app/pages/premium/saved-posts/[domain].vue row-building pipeline via PremiumCloudRepository; this PR layers blocklist filtering into that same pipeline.
  • Rule-34/App#135: Changes how fetched posts are filtered before being flattened into render rows in app/pages/posts/[domain]/index.vue and app/pages/premium/saved-posts/[domain].vue via isRenderablePost, the same extension point this PR modifies.
  • Rule-34/App#130: Modifies test/pages/posts-tag-collections.test.ts to cover custom blocklist/tag behavior; this PR updates the same test suite for post visibility assertions after blocklist filtering.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix blocklist filtering from page state' directly captures the core refactoring: moving blocklist filtering from the query layer to page-level state, which is the main technical change across the posts and saved-posts components.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dependency-update-adoption
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch dependency-update-adoption

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/pages/posts/[domain]/index.vue (1)

541-545: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve non-renderable post guarding for downstream consumers.

Line 541 removes query-level normalization, but other consumers still read raw data.pages[*].data (e.g., schema mapping) and dereference post.high_res_file.*. If non-renderable posts are present, this can throw at runtime.

Proposed fix
-const firstPostsPageAsSchema = computed(() => {
-  const firstPagePosts = data.value?.pages[0]?.data ?? []
+const firstPostsPageAsSchema = computed(() => {
+  const firstPagePosts = (data.value?.pages[0]?.data ?? []).filter(isRenderablePost)

   return firstPagePosts.slice(0, 8).map((post) => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/pages/posts/`[domain]/index.vue around lines 541 - 545, Removing the
maxPages setting eliminates query-level normalization, allowing non-renderable
posts to reach downstream consumers that access post.high_res_file properties
without proper guarding. Add filtering or existence checks in the code that
processes data.pages[*].data and accesses post.high_res_file to ensure
non-renderable posts are either filtered out before consumption or safely
handled with null/undefined checks to prevent runtime errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@app/pages/posts/`[domain]/index.vue:
- Around line 541-545: Removing the maxPages setting eliminates query-level
normalization, allowing non-renderable posts to reach downstream consumers that
access post.high_res_file properties without proper guarding. Add filtering or
existence checks in the code that processes data.pages[*].data and accesses
post.high_res_file to ensure non-renderable posts are either filtered out before
consumption or safely handled with null/undefined checks to prevent runtime
errors.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6254fd3a-6158-426d-9ccf-29b684f8e19b

📥 Commits

Reviewing files that changed from the base of the PR and between 8dc1ac4 and 8af24c5.

📒 Files selected for processing (19)
  • AGENTS.md
  • app/assets/js/post-blocklist.ts
  • app/pages/posts/[domain]/index.vue
  • app/pages/premium/saved-posts/[domain].vue
  • i18n/locales/de.json
  • i18n/locales/en.json
  • i18n/locales/es.json
  • i18n/locales/fr.json
  • i18n/locales/id.json
  • i18n/locales/it.json
  • i18n/locales/ja.json
  • i18n/locales/ko.json
  • i18n/locales/pt.json
  • i18n/locales/ru.json
  • i18n/locales/tr.json
  • i18n/locales/vi.json
  • i18n/locales/zh.json
  • test/pages/posts-tag-collections.test.ts
  • test/pages/premium-cloud.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,js,vue,json,md}

📄 CodeRabbit inference engine (AGENTS.md)

Prettier settings: 120-char print width, no semicolons, single quotes, trailing commas removed, single attribute per line in Vue templates

Files:

  • i18n/locales/fr.json
  • i18n/locales/de.json
  • i18n/locales/es.json
  • i18n/locales/vi.json
  • i18n/locales/en.json
  • i18n/locales/pt.json
  • i18n/locales/tr.json
  • app/assets/js/post-blocklist.ts
  • i18n/locales/ja.json
  • i18n/locales/ru.json
  • test/pages/posts-tag-collections.test.ts
  • app/pages/premium/saved-posts/[domain].vue
  • i18n/locales/zh.json
  • i18n/locales/it.json
  • i18n/locales/ko.json
  • test/pages/premium-cloud.test.ts
  • i18n/locales/id.json
  • AGENTS.md
  • app/pages/posts/[domain]/index.vue
**/*.{ts,js,vue}

📄 CodeRabbit inference engine (AGENTS.md)

For URL validation/parsing, prefer URL.canParse() or URL.parse() over constructor try/catch. Use URL.parse() when the parsed URL object is needed, with a URL.canParse() fallback in browser code if compatibility matters

Files:

  • app/assets/js/post-blocklist.ts
  • test/pages/posts-tag-collections.test.ts
  • app/pages/premium/saved-posts/[domain].vue
  • test/pages/premium-cloud.test.ts
  • app/pages/posts/[domain]/index.vue
**/*.{ts,vue}

📄 CodeRabbit inference engine (AGENTS.md)

Use PocketBase batch writes for multi-record replacement/reorder operations. Reordering positioned records should use the batch API instead of one HTTP write per changed row

Files:

  • app/assets/js/post-blocklist.ts
  • test/pages/posts-tag-collections.test.ts
  • app/pages/premium/saved-posts/[domain].vue
  • test/pages/premium-cloud.test.ts
  • app/pages/posts/[domain]/index.vue
test/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

test/**/*.test.ts: Tests use @nuxt/test-utils with Playwright inside describe blocks that call await setup({ browser: true })
For debugging tests, import debugBrowserOptions from test/helper.ts for headful playback with slowMo
Plain Vitest suites that import app modules directly do not get Nuxt's runtime alias resolution. Keep repository/pure modules importable through relative paths or import them directly from their app path
@nuxt/test-utils $fetch has no .raw. For redirect status and Location headers, use fetch from @nuxt/test-utils with { redirect: 'manual' }
Locale-related tests should import localeCodes, prefixedLocaleCodes, and removedLocaleCodes from config/i18n instead of hardcoding locale lists

Files:

  • test/pages/posts-tag-collections.test.ts
  • test/pages/premium-cloud.test.ts
test/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Server-side API calls are mocked via a test-only Nitro plugin at test/server-mocks/plugin.ts, injected through nuxt.config.ts → $test.nitro.plugins

Files:

  • test/pages/posts-tag-collections.test.ts
  • test/pages/premium-cloud.test.ts
**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

**/*.vue: Component auto-imports are registered without path prefix (pathPrefix: false). Import components as <DomainSelector> not <Input/DomainSelector>
SSR canonical <link> fixes must use server/plugins/fix-canonical-queries.ts to patch rendered HTML due to canonicalQueries being a no-op in i18n v10. CSR fixes use useHead in page components.
Schema.org breadcrumb source item URLs should stay local/locale-relative. Do not convert breadcrumb items in page code to project.urls.production; nuxt-schema-org resolves the rendered JSON-LD output against site.url
Query filters use flat bracket keys (filter[sort], filter[rating], etc.) with Vue Router's default query handling. Do not re-add qs for nested route.query.filter objects unless the URL contract changes
useLazyToast() lazy-loads vue-sonner and renders ClientToaster on first use. The first toast must wait for ClientToaster to mount before calling toast.*; use a plain nextTick() check to ensure the component has loaded
When a premium prompt is triggered from inside a bottom sheet or dialog, let the sheet/dialog owner close the local UI, open the premium prompt, and restore the local UI after the prompt closes. Opening directly from a nested child can leave stacked dialogs
Preserve interaction-gated loading for post UI features. Components, composables, and heavy dependencies that are only needed after a user opens a menu/sheet/dialog should stay behind Nuxt Lazy* components or dynamic imports
Keep @formkit/auto-animate route-scoped unless it is used broadly. Use local vAutoAnimate imports on premium CSR pages to avoid loading the runtime in the first-load entry
VueUse moveArrayElement() applies the array move on nextTick. For state that is immediately persisted, build the reordered array synchronously instead of reading it before VueUse has applied the move

Files:

  • app/pages/premium/saved-posts/[domain].vue
  • app/pages/posts/[domain]/index.vue
app/pages/**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

app/pages/**/*.vue: Page-specific tags (title, description) should use useSeoMeta in the page component
Premium auth transitions are reload-backed in the dashboard/sign-in flow. Premium sync state does not need per-user owner scoping in useState. Rely on the page reload to clear memory state
Active blocklist filtering is client-only visibility state. Keep TanStack infinite-query data raw and apply blocklist hiding in page-level computed rows. Do not mutate query pages in select with selectedBlockList

Files:

  • app/pages/premium/saved-posts/[domain].vue
  • app/pages/posts/[domain]/index.vue
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-22T11:09:39.903Z
Learning: Locales are defined in `config/i18n.ts` as the single source of truth. Retired prefixes live in `removedLocaleCodes` and 301 redirect via `server/middleware/redirect-removed-locales.ts`
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-22T11:09:39.903Z
Learning: Do not conflate Matomo and GSC analytics when deciding locale retention. Use Performance → Add filter → URLs containing `/locale/` (no trailing wildcard) in GSC; avoid `+/locale/*` which reports false zeros
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-22T11:09:39.903Z
Learning: Each active locale is ongoing translation, QA, and SEO surface area. Do not expand locales without GSC evidence of organic demand. Retire prefixes with negligible GSC traffic via `removedLocaleCodes` + permanent 301
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-22T11:09:39.903Z
Learning: Prefer Nitro/server for URL, SEO, and redirect behavior unless the feature genuinely needs client interactivity. Crawlers, bookmarks, and cold loads should get the correct response on the first HTTP round-trip
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-22T11:09:39.903Z
Learning: Do not add client middleware 'for parity' when the server already handles the case. Retired-locale redirects are the reference pattern: one file, server-only, inlined and config-driven
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-22T11:09:39.903Z
Learning: Canonical URLs must point to production (`https://r34.app/…`) even when served from clone domains. Use `project.urls.production` for canonicals
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-22T11:09:39.903Z
Learning: Non-premium SPA navigations keep the direct image path. Validate image delivery in an environment where imgproxy can resolve the source URL
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-22T11:09:39.903Z
Learning: Server over client when equivalent. Every global route middleware, duplicated redirect helper, and client-only SEO shim is bundle + hydration cost. Default to Nitro middleware, server plugins, and SSR head tags
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-22T11:09:39.903Z
Learning: Prefer high-impact, measurable optimizations over small rewrites. Keep battle-tested dependencies unless replacing one has a clear, measured payoff
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-22T11:09:39.903Z
Learning: After substantial performance changes, verify with a production build, relevant tests, request traces, and Lighthouse against the built app before deciding the change is worth keeping
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-22T11:09:39.903Z
Learning: Production is behind Cloudflare which Brotli-compresses HTML responses. Do not add app-level HTML compression unless a direct-origin deployment needs it and the change is verified with headers, byte sizes, warm TTFB, and Lighthouse
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-22T11:09:39.903Z
Learning: The service worker is intentionally disabled (`selfDestroying: true`). Do not add service worker logic
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-22T11:09:39.903Z
Learning: Source map uploads only happen in production Docker builds (needs `SENTRY_ORG`, `SENTRY_PROJECT`, `SENTRY_AUTH_TOKEN` build args)
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-22T11:09:39.903Z
Learning: For premium and PocketBase flows, use a real authenticated browser session for final investigation when possible. Unit tests can prove payload and repository behavior, but real-browser traces catch request bursts and state changes
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-22T11:09:39.903Z
Learning: Empty cloud state means 'no user-authored cloud override'. Do not seed PocketBase from local defaults during initial load. Only write premium cloud records after explicit user edits
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-22T11:09:39.903Z
Learning: PocketBase realtime subscriptions echo local writes. When debugging sync performance, inspect real network traces and separate write requests from realtime-triggered refreshes
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-22T11:09:39.903Z
Learning: Saved posts use the same premium cloud realtime runtime as tag collections, custom boorus, and the custom blocklist. Empty saved-post cloud state means there are no saved posts
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-22T11:09:39.903Z
Learning: Multi-stage Docker build: build stage uses `pnpm install --frozen-lockfile`. Source map uploads need `SENTRY_ORG`, `SENTRY_PROJECT`, and `SENTRY_AUTH_TOKEN`; set `SENTRY_UPLOAD_SOURCE_MAPS=false` to skip them
Learnt from: CR
Repo: Rule-34/App

Timestamp: 2026-06-22T11:09:39.903Z
Learning: Do not carry local dependency patches (`pnpm patch`, `patch-package`, `patchedDependencies`) in this repo. For third-party bugs, prefer an upstream release, a supported configuration/workaround, or ignoring the Sentry issue
📚 Learning: 2026-06-22T07:50:54.935Z
Learnt from: AlejandroAkbal
Repo: Rule-34/App PR: 144
File: test/server/fix-canonical-queries.test.ts:174-178
Timestamp: 2026-06-22T07:50:54.935Z
Learning: In this Nuxt 3/4 project using `nuxt-schema-org` (v6.2.1), breadcrumb `source`/`itemListElement` URL values may be provided via `localePath()` (locale-relative in source code), but `nuxt-schema-org` renders JSON-LD with production-absolute URLs using `site.url` during schema rendering. When asserting JSON-LD breadcrumb `itemListElement` URLs in tests, expect production-absolute URLs (e.g., prefix with `project.urls.production.origin`), not the locale-relative `localePath()` forms. Do not flag production-absolute URL strings in JSON-LD breadcrumb tests as incorrect.

Applied to files:

  • test/pages/posts-tag-collections.test.ts
  • test/pages/premium-cloud.test.ts
🔇 Additional comments (19)
test/pages/posts-tag-collections.test.ts (1)

198-208: LGTM!

Also applies to: 226-231

test/pages/premium-cloud.test.ts (1)

151-170: LGTM!

i18n/locales/en.json (1)

100-100: LGTM!

i18n/locales/de.json (1)

100-100: LGTM!

i18n/locales/es.json (1)

100-100: LGTM!

i18n/locales/fr.json (1)

100-100: LGTM!

i18n/locales/tr.json (1)

100-100: LGTM!

i18n/locales/vi.json (1)

100-100: LGTM!

i18n/locales/zh.json (1)

100-100: LGTM!

i18n/locales/id.json (1)

100-100: LGTM!

i18n/locales/it.json (1)

100-100: LGTM!

i18n/locales/ja.json (1)

100-100: LGTM!

i18n/locales/ko.json (1)

100-100: LGTM!

i18n/locales/pt.json (1)

100-100: LGTM!

i18n/locales/ru.json (1)

100-100: LGTM!

AGENTS.md (1)

254-256: LGTM!

app/assets/js/post-blocklist.ts (1)

3-19: LGTM!

app/pages/posts/[domain]/index.vue (1)

10-10: LGTM!

Also applies to: 575-575, 586-602

app/pages/premium/saved-posts/[domain].vue (1)

10-10: LGTM!

Also applies to: 41-41, 389-390, 400-416

@AlejandroAkbal
AlejandroAkbal merged commit 45746ca into main Jun 23, 2026
1 check passed
@AlejandroAkbal
AlejandroAkbal deleted the dependency-update-adoption branch June 23, 2026 06:47
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