Skip to content

Shrink icons and the share image, and name missing share links - #199

Merged
Programcode merged 3 commits into
mainfrom
cursor/c3-assets-seo-149f
Oct 22, 2026
Merged

Programcode merged 3 commits into
mainfrom
cursor/c3-assets-seo-149f

Conversation

@Programcode

Copy link
Copy Markdown
Owner

Draft. Do not merge, and do not enable auto-merge. Merging to main deploys the frontend to production on Vercel. No migration, Edge Function, or backend deploy is part of this change.

C3 batch from the 22 Oct 2026 QA pass. One commit, cbd3bf13, on top of b79f8581.

PF-08 — favicon and share image

public/favicon.ico was a 279,465-byte 1024×1024 PNG, byte-identical to favicon.png. hero-visual.png was a 226,968-byte 1920×1080 JPEG stored under a .png name.

File Before After
favicon.ico 279,465 (PNG 1024) 6,263 (ICO 16/32/48, symbol only)
favicon.png 279,465 (1024) 3,406 (PNG 48×48)
hero-visual.png 226,968 (JPEG 1920×1080) 105,734 (JPEG q80 progressive, 1200×630)
apple-touch-icon.png — 7,295 (180×180)
icon-1024.png — 279,465 (unchanged master, nothing links it yet)
favicon-72.png 3,974 unchanged

index.html and public/embed/debate-timer/index.html point rel=icon at the ICO and the 48px PNG, and apple-touch-icon at 180px. The service worker precache drops the hero (no page renders it) and picks up the new icons. Cache name is debate-lab-v5 so clients drop the old shell. vercel.json cache headers were not edited (T2 owns that array). The existing names (favicon.ico, favicon.png, favicon-72.png, hero-visual.png) still match the 30-day rule.

hero-visual.png is still JPEG bytes with a .png extension, which is how it shipped before. Chromium decodes it at 1200×630. Renaming it to .jpg would need src/prerender/entry.tsx and the header pattern, both owned by other work.

PF-12 — manifest icons

public/manifest.webmanifest now has id /app (same as start_url) and three icons: icon-192.png (8,027, any), icon-512.png (28,727, any), icon-maskable-512.png (19,054, ~20% padding). It no longer points at the 1024 favicon. Those three are in the service-worker precache because the old list already precached icons. A local DevTools check decoded each size and reported no manifest console warnings. Lighthouse was run for performance only, so the installability category was not scored.

SE-07 — obsolete meta and image tags

Removed meta name=keywords and meta name=language from index.html. applyAppLanguage no longer writes the language meta. Guide HTML never emitted either tag.

og:image:width 1200, og:image:height 630, og:image:alt, twitter:image, and twitter:image:alt are on the shell (prerendered pages inherit them) and in scripts/generate_seo_content.py. All 36 guide pages were regenerated. The diff on each guide is those four meta lines. No marketing or guide copy changed. Per-page unique OG images were not generated; one share image carries the size and alt.

SE-08 — missing share links

SharedPage renders a not-found heading ("Shared link not found"), sets the document title itself, sets noindex, nofollow, and links home. The response stays HTTP 200. Covered by src/test/pages/SharedPage.test.tsx, and confirmed in the built app at /share/doesnotexist123 (title and heading match, robots noindex, status 200). use-page-head.ts was not edited.

/index.html is not redirected. The service worker fetches it in APP_SHELL and stores navigations under that cache key; a 308 would make cache.put of a redirected response fail. The URL already has a canonical.

/app/workspaces is unchanged. robots.txt disallows /app, the shell is noindex, and fixing the route means App.tsx or the Vercel rewrites, which this batch does not own.

SE-09 — llms.txt

Already fixed. Every sitemap guide slug is in public/llms.txt, generated by llms_txt(). check-site reported sitemap and llms.txt resolve, 45 sitemap URLs. No generator or copy change.

PF-11 — source maps, reflow, CSS

Source maps stay as they are: vite.config.ts emits hidden maps only when a Sentry upload is configured, and Sentry deletes them after upload. The verify build had no SENTRY_* values and dist contained zero .map files. The Lighthouse "missing source maps" warning is accepted. No public maps.

The app-shell header no longer reads getBoundingClientRect on scroll. A ResizeObserver border box sets --app-header-bottom. theme-init.js stays a blocking script (it prevents the theme flash) and was not edited. seo-pages.css is 3KB and is the only stylesheet on guide pages, so it stays blocking. No critical-CSS tooling.

FlowchartPage still reads getBoundingClientRect, getComputedStyle, and offsetWidth/offsetHeight. Left for T9. OnboardingTour reads layout only when a tour starts.

Lighthouse 12.8.2, local dist, performance only, after this change. This is not a paired before on the same machine. The QA report's production numbers were desktop LCP 0.71s on / and 0.91s on /app, unthrottled. Local mobile uses simulated throttling, and unsigned /app is the login shell, not HomePage.

URL Score LCP Forced reflow Notes
/ desktop 0.84 1.8s 39.6ms, source [unattributed] Image audits all score 1. Render-blocking: theme-init ~81ms, site CSS ~561ms.
/ mobile 0.56 9.9s still flagged Render-blocking savings ~900–1050ms.
/app desktop (signed out) 0.89 1.6s score 1, empty theme-init ~81ms, CSS ~321ms.
/app mobile 0.57 8.7s passes TBT 70ms.

Landing components have no layout read that matches the 40ms unattributed reflow. Authenticated /app and /app/flowchart were not traced: a stub session was rejected by the production Supabase URL baked into the build.

Verify

npm run verify passed locally (EXIT:0). GitHub Actions minutes are exhausted, so this is a local pass, not a CI pass.

  • docs:check, lint (eslint, knip, check-repo-rules), typecheck: passed
  • unit: 66 files, 448 tests passed
  • collaboration: 5 files, 30 tests passed
  • edge: Deno 2.9.6, 406 passed, 0 failed
  • build: prerender, sitemap 45 URLs, check-site 48 HTML files, no problems
  • service worker offline: upgrade, visited route, recovery, prefetch, logo all true
  • e2e smoke: 43 passed (29.2s), desktop Chromium @smoke

Two earlier runs failed an unrelated hook timeout in src/test/scripts/backend-status.test.ts. This VM's global git config sets commit.gpgsign and core.fsmonitor, and that test's temp-repo commits exceed the 10s hook limit under the parallel suite. The passing run turned those two off for the process (GIT_CONFIG_COUNT=2). The test file was not modified. Deno 2.9.6 was installed because it was not on the image; that is the version CI pins.

Left open

  • vercel.json headers do not yet cover apple-touch-icon.png, icon-192.png, icon-512.png, icon-maskable-512.png, or icon-1024.png. T2 owns the headers array. Those files will not get the 30-day cache until that pattern grows.
  • JSON-LD logo in src/prerender/entry.tsx still points at /favicon.png, now 48×48. Google wants a logo of at least 112px. The master is at /icon-1024.png for a one-line follow-up. T2 owns that file. JSON-LD image still points at /hero-visual.png, which was resized in place.
  • hero-visual.png remains JPEG bytes served as image/png.
  • /app/workspaces still returns the SPA 404 at HTTP 200.
  • /index.html is not redirected, for the service-worker reason above.
  • No per-page OG images.
  • Render-blocking CSS and theme-init.js remain. Landing reflow (~40ms, unattributed) and Flowchart layout reads remain.
  • Non-guide sitemap URLs (/privacy, /terms, /tools and its tools, /expocit/sources) are still absent from llms.txt. The QA bug was the 36 guides, which are all listed.
Open in Web Open in Cursor 

A real 16/32/48 favicon and a 1200x630 share image replace the 1024px PNG and the 1920x1080 hero. Public pages describe that image, and a missing share reads as not found.

Co-authored-by: Gáborjáni-Szabó Ádám <Programcode@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

Next included review available in 45 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
📥 Commits

Reviewing files that changed from the base of the PR and between 6962616 and 2b70f0d.

⛔ Files ignored due to path filters: 9
  • public/apple-touch-icon.png is excluded by !**/*.png
  • public/favicon.ico is excluded by !**/*.ico
  • public/favicon.png is excluded by !**/*.png
  • public/hero-visual.jpg is excluded by !**/*.jpg
  • public/hero-visual.png is excluded by !**/*.png
  • public/icon-1024.png is excluded by !**/*.png
  • public/icon-192.png is excluded by !**/*.png
  • public/icon-512.png is excluded by !**/*.png
  • public/icon-maskable-512.png is excluded by !**/*.png
📒 Files selected for processing: 53
  • docs/operations.md
  • embed/debate-timer/index.html
  • index.html
  • public/ai-debate-case-generator/index.html
  • public/ai-debate-coach/index.html
  • public/ai-debate-practice/index.html
  • public/ai-rebuttal-generator/index.html
  • public/ai-vs-traditional-debate-prep/index.html
  • public/argument-flowchart/index.html
  • public/best-ai-debate-tools/index.html
  • public/british-parliamentary-debate/index.html
  • public/congress-debate/index.html
  • public/debate-block-file-guide/index.html
  • public/debate-clash-points/index.html
  • public/debate-collapsing-strategy/index.html
  • public/debate-evidence-finder/index.html
  • public/debate-fallacies/index.html
  • public/debate-judge-adaptation/index.html
  • public/debate-motion-analysis/index.html
  • public/debate-prep-time-management/index.html
  • public/debate-pronunciation-and-delivery/index.html
  • public/debate-rebuttal-practice/index.html
  • public/debate-speech-structure/index.html
  • public/debate-topics-for-students/index.html
  • public/debatelab-pricing/index.html
  • public/debatelab-vs-chatgpt/index.html
  • public/debatelab-vs-debateclub/index.html
  • public/debatelab-vs-windebate/index.html
  • public/how-to-cross-examine-in-debate/index.html
  • public/how-to-flow-in-debate/index.html
  • public/how-to-practice-rebuttals/index.html
  • public/how-to-prepare-for-a-debate-tournament/index.html
  • public/how-to-weigh-in-debate/index.html
  • public/how-to-write-a-debate-case/index.html
  • public/lincoln-douglas-debate/index.html
  • public/manifest.webmanifest
  • public/online-debate-timer/index.html
  • public/policy-debate/index.html
  • public/public-forum-debate/index.html
  • public/sw.js
  • public/world-schools-debate/index.html
  • scripts/generate_seo_content.py
  • scripts/test-service-worker-offline.mjs
  • src/components/layout/AppLayout.tsx
  • src/i18n.ts
  • src/locales/en.json
  • src/locales/es.json
  • src/locales/hu.json
  • src/pages/SharedPage.tsx
  • src/prerender/entry.tsx
  • src/test/pages/SharedPage.test.tsx
  • src/test/vercel-deployment-config.test.ts
  • vercel.json
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Essentials by visiting https://app.coderabbit.ai/settings/billing.

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

@Programcode
Programcode marked this pull request as ready for review October 22, 2026 23:19
Copilot AI balanced review requested due to automatic review settings October 22, 2026 23:19
@Programcode
Programcode merged commit 1b00d89 into main Oct 22, 2026
3 of 10 checks passed

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Programcode added a commit that referenced this pull request Oct 23, 2026
…02, PF-04, PF-05, PF-06, PF-09) (#215)

* Cache every public icon file for 30 days

C3 (#199) added icon-1024.png and the 192/512/maskable manifest icons,
which still fell back to revalidating on every request. The test now
reads public/ so a new icon size can't miss the rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Load only the active locale and keep app-only code off public pages

- Spanish and Hungarian strings are their own chunks, fetched when chosen;
  English stays in the entry as the default and fallback. The first render
  waits for the chosen language, and a later choice wins over one still
  loading.
- AppLayout (header, navigation, debate dialogs) loads only under /app.
  mountApp starts the page chunk and the layout while the entry starts,
  instead of when React first reaches them.
- The landing reveal is a CSS transition, so / no longer loads
  framer-motion's 118 KB chunk.
- The session check and layout load show the route's skeleton instead of a
  full-screen spinner.
- check-build-boundaries fails if the es/hu catalogs or AppLayout re-enter
  the entry or the landing, login, home, generate or attack graphs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Open the main app pages offline and keep each page's own HTML

The build writes a precache list into sw.js from the Vite manifest: the
shell's scripts and styles, the signed-in layout, Home, Library, History,
Timer and Settings, the es/hu strings and Inter's Latin fonts. A missing
manifest key fails the build.

Offline, app routes get the precached app-shell.html (the paths
vercel.json rewrites) and a public page gets only its own HTML, stored
under its own path. Before, every navigation was stored as /index.html,
so any route could come back as the last page visited. /auth/callback,
Supabase paths and /_vercel/ stay network-only. Caches carry the build's
id and the next build's worker deletes them.

vite preview applies the vercel.json rewrites, so the offline test loads
what production serves. The test's async waitForFunction calls passed at
once without checking (Playwright treats the returned Promise as truthy);
they now poll.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Hold Supabase reads while offline instead of retrying them

Offline, supabase-js retried every failed read three more times, so
opening Settings sent about 70 doomed requests in 30 seconds (user_roles
four times, user_settings 18). REST, Functions and Storage reads now wait
for the 'online' event and go out once; an abort still ends them. Writes
fail at once as before, and Auth is left alone because auth-js holds its
session lock during a user lookup.

TanStack Query also starts with the browser's real online state, so a page
opened offline pauses its queries instead of running them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Don't reload for a missing chunk while offline

Choosing a language whose strings were never cached, offline, failed its
import and triggered the stale-deploy reload, which without a service
worker replaces the page with the browser's offline error. Offline the
chunk is unreachable, not stale, so the error stands and the app keeps
the current language.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Treat /expocit/sources as a public page offline, and always leave the startup shell

/expocit/sources is prerendered and Vercel serves that file before the
rewrite, so offline it now gets its own stored HTML instead of the shell
(whose page chunk is not precached). vite preview serves prerendered
pages before the rewrites too. The non-prerendered first render now also
runs if waiting for the language rejects.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Disable Upgrade Now while offline and say why

Checkout needs the network. Offline the button stays disabled with a short
notice under it (one new string, pricingModal.offlineUpgrade, in all three
catalogs) and re-enables on the 'online' event. Done here now that T4
(#207) has merged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Load every language before the flowchart opens

T9 (#206) recognises flowchart starter text saved in any language by
comparing it with each catalog. With Spanish and Hungarian loaded only on
demand, a chart saved in Hungarian and opened in English kept its
Hungarian starter text. The flowchart route's loader now fetches every
catalog before the page renders. Only that page (and a hover prefetch of
it) pays for them, and they are precached for offline.

The flowchart tests now load through the route loader; without this
change four of them fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Address review round 1: stop retries offline instead of holding reads

- Offline Supabase reads are no longer held until 'online'. A held read kept
  the token of whoever started it and could replay after sign-out, and a
  browser that wrongly reports offline would never send it. Every request
  is now sent; one that fails while the browser is offline ends as an
  abort, which postgrest-js does not retry. TanStack Query is no longer
  seeded from navigator.onLine; failed queries refetch on 'online' instead.
- A language chosen offline before its strings were cached is applied when
  the connection returns.
- The first render waits at most 3 s for the language's strings.
- The service worker's build id also covers the unhashed public files it
  serves cache-first (theme-init.js, icons), and an offline public page
  prefers the copy saved on the last visit over the one stored at install.
- The landing page's code is precached with its HTML, so / opened offline
  is not replaced by the error page.
- test:sw now visits /auth/callback before checking it is never cached,
  and checks the precached files sit in the install-time shell cache.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Address review round 2: load layout and page together after sign-in

- After a client-side navigation into /app (sign-in, the callback), the
  page chunk now starts with the layout chunk instead of after it.
- The offline no-retry rule also matches reads through the dev:preview
  staging proxy path.
- hero-visual.jpg and placeholder.svg also feed the worker's build id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.

3 participants