fix: feed image thumbnails + mobile layout regressions - #1332

Merged
NiallJoeMaher merged 3 commits into
developfrom
fix/feed-images-and-mobile-layout
Jun 14, 2026
Merged

fix: feed image thumbnails + mobile layout regressions#1332
NiallJoeMaher merged 3 commits into
developfrom
fix/feed-images-and-mobile-layout

Conversation

@NiallJoeMaher

Copy link
Copy Markdown
Contributor

Follow-up fixes after the relaunch redesign merge.

1. Broken feed thumbnails (doubled image URLs)

HackerNoon's RSS media:thumbnail/media:content URLs are malformed at the source — an already-absolute CDN URL prefixed with their own origin (https://hackernoon.com/https://cdn.hackernoon.com/…), which 404s. Ingestion stored them verbatim, and because the redesigned cards SSR the <img>, the broken-image icon stuck (the error event fires before React hydrates and attaches onError).

  • unwrapDoubledUrl() helper in utils/url.ts (+ unit tests)
  • Card unwraps at render and detects pre-hydration failures via a ref callback → a dead image collapses to no thumbnail instead of a broken icon
  • Sanitised at ingestion (fetch-rss, admin/sync-feeds — media + OG image)
  • One-off scripts/fix-doubled-image-urls.ts to scrub already-stored rows (display is fixed without it; this cleans data server-side SEO/OG reads)

2. Mobile layout regressions

  • No horizontal overflow — byline meta truncates; card padding p-4 on mobile
  • Side gutters.app-main mobile gutter → 0.75rem; per-page content wrappers px-4 py-8px-0 py-4 on mobile (parent already gutters), so text gets full reading width
  • Top bar collapses to burger + logo + search icon ≤720px; Log in / Join free move into the nav drawer; real search SVG replaces the tiny glyph
  • Feed filters drop to their own row below the tabs and no longer sit in an overflow container that clipped the dropdowns (they now open on mobile)
  • Command palette Esc hint → X close button on mobile
  • Search field active state is a subtle accent underline, not the global (blue-resolving) focus ring

Verification

  • utils/url.test.ts passes (6/6); ESLint + tsc clean
  • Verified at 390px via Playwright: 0 horizontal overflow, 25 cards, top-bar/drawer/filter-dropdown/palette-close all confirmed

🤖 Generated with Claude Code

@NiallJoeMaher
NiallJoeMaher requested a review from a team as a code ownerJune 14, 2026 07:56
@vercel

vercelBot commented Jun 14, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
coduReadyReadyPreview, CommentJun 14, 2026 8:06am

Request Review

@coderabbitai

coderabbitaiBot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8dbbf420-a62e-4262-9d19-71429ff30111

📥 Commits

Reviewing files that changed from the base of the PR and between 4678f5a and cd2679d.

📒 Files selected for processing (25)
  • app/(app)/[username]/[slug]/_userLinkDetail.tsx
  • app/(app)/admin/_client.tsx
  • app/(app)/admin/moderation/_client.tsx
  • app/(app)/admin/sources/_client.tsx
  • app/(app)/admin/tags/_client.tsx
  • app/(app)/admin/users/_client.tsx
  • app/(app)/company/[slug]/page.tsx
  • app/(app)/draft/[id]/page.tsx
  • app/(app)/feed/_client.tsx
  • app/(app)/s/[sourceSlug]/[slug]/_feedArticleContent.tsx
  • app/(app)/s/[sourceSlug]/_sourceProfileClient.tsx
  • app/(app)/tag/[slug]/page.tsx
  • app/api/admin/sync-feeds/route.ts
  • components/CommandPalette/CommandPalette.tsx
  • components/ContentDetail/Layout.tsx
  • components/ContentDetail/PostReader.tsx
  • components/Feed/Filters.tsx
  • components/Layout/NavDrawer.tsx
  • components/Layout/TopBar.tsx
  • components/UnifiedContentCard/UnifiedContentCard.tsx
  • scripts/fetch-rss.ts
  • scripts/fix-doubled-image-urls.ts
  • styles/globals.css
  • utils/url.test.ts
  • utils/url.ts

Walkthrough

This PR adds an unwrapDoubledUrl URL-normalization utility and applies it across RSS sync, fetch-rss script, and UnifiedContentCard, plus a one-off database cleanup script. Separately, it updates responsive padding (px-0 py-4 sm:px-4 sm:py-8) across all page containers, refactors TopBar search into reusable internal components, adds auth buttons to NavDrawer for logged-out users, and improves CommandPalette's mobile close affordance.

Changes

Doubled image URL normalization

Layer / File(s)Summary
unwrapDoubledUrl utility and tests
utils/url.ts, utils/url.test.ts
Adds DOUBLED_ORIGIN regex and exported unwrapDoubledUrl function, covered by a Vitest suite including edge cases and composition with ensureHttps.
One-off database fix script
scripts/fix-doubled-image-urls.ts
New script scans posts.coverImage and aggregated_article.imageUrl/ogImageUrl for doubled-URL patterns, normalizes via unwrapDoubledUrl + ensureHttps, updates changed rows, and exits 0/1.
URL normalization wired into sync, fetch-rss, and content card
app/api/admin/sync-feeds/route.ts, scripts/fetch-rss.ts, components/UnifiedContentCard/UnifiedContentCard.tsx
Integrates unwrapDoubledUrl + ensureHttps into extractImageUrl, post-OG-fetch normalization, fetch-rss image derivation, and UnifiedContentCard image URL computation plus SSR hydration broken-image ref.

Mobile-responsive layout and navigation UX

Layer / File(s)Summary
Global CSS and page-level responsive padding
styles/globals.css, app/(app)/[username]/[slug]/_userLinkDetail.tsx, app/(app)/admin/_client.tsx, app/(app)/admin/moderation/_client.tsx, app/(app)/admin/sources/_client.tsx, app/(app)/admin/tags/_client.tsx, app/(app)/admin/users/_client.tsx, app/(app)/company/[slug]/page.tsx, app/(app)/draft/[id]/page.tsx, app/(app)/s/[sourceSlug]/[slug]/_feedArticleContent.tsx, app/(app)/s/[sourceSlug]/_sourceProfileClient.tsx, app/(app)/tag/[slug]/page.tsx
Updates .app-main mobile padding to a three-value shorthand in CSS, then applies px-0 py-4 sm:px-4 sm:py-8 to all page-level containers.
Shared ContentDetail and Feed layout
components/ContentDetail/Layout.tsx, components/ContentDetail/PostReader.tsx, components/Feed/Filters.tsx, app/(app)/feed/_client.tsx
Applies responsive padding to ContentDetailLayout and PostReader; updates FeedFilters to a wrapping right-justified flex layout and moves the signed-in filter cluster into a full-width mobile row.
TopBar search icon refactor and mobile auth hide
components/Layout/TopBar.tsx
Extracts SearchIcon SVG and SearchIconButton internal components, shows desktop search only at min-[721px], adds mobile SearchIconButton for both auth states, and hides login/join buttons on mobile.
NavDrawer mobile auth CTA buttons
components/Layout/NavDrawer.tsx
Adds "Join free" and "Log in" buttons inside NavDrawer for logged-out sessions; each button calls onClose() then signIn().
CommandPalette mobile close and content card layout
components/CommandPalette/CommandPalette.tsx, components/UnifiedContentCard/UnifiedContentCard.tsx
Replaces CommandPalette's always-visible Esc hint with a desktop kbd hint plus mobile X-icon close button; adjusts UnifiedContentCard outer padding and author/byline truncation classes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • codu-code/codu#1324: Modifies _userLinkDetail.tsx, the same file where this PR adjusts responsive wrapper padding across loading, error, and main article states.
  • codu-code/codu#1322: This PR's sync-feeds/route.ts URL normalization directly extends the RSS sync ingest logic introduced in that PR.

Poem

🐰 Hop hop, the URLs are clean,
No doubled origins to be seen!
On mobile screens the padding shrinks,
A search icon button winks and blinks.
The drawer now greets guests with flair—
"Join free!" says the bunny with floppy ears! 🌟

✨ 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 fix/feed-images-and-mobile-layout

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 and usage tips.

NiallJoeMaherand others added 2 commits June 14, 2026 09:03
HackerNoon's RSS media:thumbnail/media:content URLs are malformed at the
source — an already-absolute CDN URL prefixed with their own origin
(https://hackernoon.com/https://cdn.hackernoon.com/…), which 404s. Our
ingestion stored them verbatim, and the redesigned cards SSR the <img>,
so the broken-image icon stuck: the error event fires before React
hydrates and attaches onError, so the fallback never runs.
- add unwrapDoubledUrl() (utils/url.ts) + unit tests
- card: unwrap at render and detect pre-hydration failures via a ref
callback, so a dead image collapses to no thumbnail (not a broken icon)
- sanitise URLs at ingestion (fetch-rss, admin/sync-feeds — media + OG)
- one-off scrub script for already-stored rows
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…reens
The relaunch redesign had several mobile regressions:
- cards overflowed the viewport (byline meta line couldn't shrink) — now
truncates; card padding p-4 on mobile
- app-main side gutters reduced to 0.75rem and per-page content wrappers
drop px-4 py-8 → px-0 py-4 on mobile (parent already gutters), so text
gets full reading width without double padding
- top bar collapses to burger + logo + search icon ≤720px; Log in / Join
free move into the nav drawer; real search SVG replaces the tiny glyph
- feed filters drop to their own row below the tabs instead of cramming
under them, and no longer sit in an overflow container that clipped the
FilterPill dropdowns (they now open on mobile)
- command palette: Esc hint becomes an X close button on mobile
- search field active state is a subtle accent underline, not the global
(blue-resolving) focus ring
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code review caught that the naive lastIndexOf scan would truncate a URL
whose path legitimately contains http://https:// deeper down (path-based
image proxies, or a slug literally containing a scheme). Match only an
absolute URL sitting immediately after the host — the exact shape of the
upstream doubled-origin bug — and leave everything else untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@NiallJoeMaher
NiallJoeMaherforce-pushed the fix/feed-images-and-mobile-layout branch from 3176a3c to cd2679dCompareJune 14, 2026 08:04
@NiallJoeMaher
NiallJoeMaher merged commit dd621f2 into developJun 14, 2026
3 of 5 checks passed
@NiallJoeMaher
NiallJoeMaher deleted the fix/feed-images-and-mobile-layout branch June 14, 2026 08:05
Sign up for freeto 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

@NiallJoeMaher
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix: feed image thumbnails + mobile layout regressions - #1332

Merged
NiallJoeMaher merged 3 commits into
developfrom
fix/feed-images-and-mobile-layout
Jun 14, 2026
Merged

fix: feed image thumbnails + mobile layout regressions#1332
NiallJoeMaher merged 3 commits into
developfrom
fix/feed-images-and-mobile-layout

Conversation

@NiallJoeMaher

Copy link
Copy Markdown
Contributor

Follow-up fixes after the relaunch redesign merge.

1. Broken feed thumbnails (doubled image URLs)

HackerNoon's RSS media:thumbnail/media:content URLs are malformed at the source — an already-absolute CDN URL prefixed with their own origin (https://hackernoon.com/https://cdn.hackernoon.com/…), which 404s. Ingestion stored them verbatim, and because the redesigned cards SSR the <img>, the broken-image icon stuck (the error event fires before React hydrates and attaches onError).

  • unwrapDoubledUrl() helper in utils/url.ts (+ unit tests)
  • Card unwraps at render and detects pre-hydration failures via a ref callback → a dead image collapses to no thumbnail instead of a broken icon
  • Sanitised at ingestion (fetch-rss, admin/sync-feeds — media + OG image)
  • One-off scripts/fix-doubled-image-urls.ts to scrub already-stored rows (display is fixed without it; this cleans data server-side SEO/OG reads)

2. Mobile layout regressions

  • No horizontal overflow — byline meta truncates; card padding p-4 on mobile
  • Side gutters.app-main mobile gutter → 0.75rem; per-page content wrappers px-4 py-8px-0 py-4 on mobile (parent already gutters), so text gets full reading width
  • Top bar collapses to burger + logo + search icon ≤720px; Log in / Join free move into the nav drawer; real search SVG replaces the tiny glyph
  • Feed filters drop to their own row below the tabs and no longer sit in an overflow container that clipped the dropdowns (they now open on mobile)
  • Command palette Esc hint → X close button on mobile
  • Search field active state is a subtle accent underline, not the global (blue-resolving) focus ring

Verification

  • utils/url.test.ts passes (6/6); ESLint + tsc clean
  • Verified at 390px via Playwright: 0 horizontal overflow, 25 cards, top-bar/drawer/filter-dropdown/palette-close all confirmed

🤖 Generated with Claude Code

@NiallJoeMaher
NiallJoeMaher requested a review from a team as a code ownerJune 14, 2026 07:56
@vercel

vercelBot commented Jun 14, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
coduReadyReadyPreview, CommentJun 14, 2026 8:06am

Request Review

@coderabbitai

coderabbitaiBot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8dbbf420-a62e-4262-9d19-71429ff30111

📥 Commits

Reviewing files that changed from the base of the PR and between 4678f5a and cd2679d.

📒 Files selected for processing (25)
  • app/(app)/[username]/[slug]/_userLinkDetail.tsx
  • app/(app)/admin/_client.tsx
  • app/(app)/admin/moderation/_client.tsx
  • app/(app)/admin/sources/_client.tsx
  • app/(app)/admin/tags/_client.tsx
  • app/(app)/admin/users/_client.tsx
  • app/(app)/company/[slug]/page.tsx
  • app/(app)/draft/[id]/page.tsx
  • app/(app)/feed/_client.tsx
  • app/(app)/s/[sourceSlug]/[slug]/_feedArticleContent.tsx
  • app/(app)/s/[sourceSlug]/_sourceProfileClient.tsx
  • app/(app)/tag/[slug]/page.tsx
  • app/api/admin/sync-feeds/route.ts
  • components/CommandPalette/CommandPalette.tsx
  • components/ContentDetail/Layout.tsx
  • components/ContentDetail/PostReader.tsx
  • components/Feed/Filters.tsx
  • components/Layout/NavDrawer.tsx
  • components/Layout/TopBar.tsx
  • components/UnifiedContentCard/UnifiedContentCard.tsx
  • scripts/fetch-rss.ts
  • scripts/fix-doubled-image-urls.ts
  • styles/globals.css
  • utils/url.test.ts
  • utils/url.ts

Walkthrough

This PR adds an unwrapDoubledUrl URL-normalization utility and applies it across RSS sync, fetch-rss script, and UnifiedContentCard, plus a one-off database cleanup script. Separately, it updates responsive padding (px-0 py-4 sm:px-4 sm:py-8) across all page containers, refactors TopBar search into reusable internal components, adds auth buttons to NavDrawer for logged-out users, and improves CommandPalette's mobile close affordance.

Changes

Doubled image URL normalization

Layer / File(s)Summary
unwrapDoubledUrl utility and tests
utils/url.ts, utils/url.test.ts
Adds DOUBLED_ORIGIN regex and exported unwrapDoubledUrl function, covered by a Vitest suite including edge cases and composition with ensureHttps.
One-off database fix script
scripts/fix-doubled-image-urls.ts
New script scans posts.coverImage and aggregated_article.imageUrl/ogImageUrl for doubled-URL patterns, normalizes via unwrapDoubledUrl + ensureHttps, updates changed rows, and exits 0/1.
URL normalization wired into sync, fetch-rss, and content card
app/api/admin/sync-feeds/route.ts, scripts/fetch-rss.ts, components/UnifiedContentCard/UnifiedContentCard.tsx
Integrates unwrapDoubledUrl + ensureHttps into extractImageUrl, post-OG-fetch normalization, fetch-rss image derivation, and UnifiedContentCard image URL computation plus SSR hydration broken-image ref.

Mobile-responsive layout and navigation UX

Layer / File(s)Summary
Global CSS and page-level responsive padding
styles/globals.css, app/(app)/[username]/[slug]/_userLinkDetail.tsx, app/(app)/admin/_client.tsx, app/(app)/admin/moderation/_client.tsx, app/(app)/admin/sources/_client.tsx, app/(app)/admin/tags/_client.tsx, app/(app)/admin/users/_client.tsx, app/(app)/company/[slug]/page.tsx, app/(app)/draft/[id]/page.tsx, app/(app)/s/[sourceSlug]/[slug]/_feedArticleContent.tsx, app/(app)/s/[sourceSlug]/_sourceProfileClient.tsx, app/(app)/tag/[slug]/page.tsx
Updates .app-main mobile padding to a three-value shorthand in CSS, then applies px-0 py-4 sm:px-4 sm:py-8 to all page-level containers.
Shared ContentDetail and Feed layout
components/ContentDetail/Layout.tsx, components/ContentDetail/PostReader.tsx, components/Feed/Filters.tsx, app/(app)/feed/_client.tsx
Applies responsive padding to ContentDetailLayout and PostReader; updates FeedFilters to a wrapping right-justified flex layout and moves the signed-in filter cluster into a full-width mobile row.
TopBar search icon refactor and mobile auth hide
components/Layout/TopBar.tsx
Extracts SearchIcon SVG and SearchIconButton internal components, shows desktop search only at min-[721px], adds mobile SearchIconButton for both auth states, and hides login/join buttons on mobile.
NavDrawer mobile auth CTA buttons
components/Layout/NavDrawer.tsx
Adds "Join free" and "Log in" buttons inside NavDrawer for logged-out sessions; each button calls onClose() then signIn().
CommandPalette mobile close and content card layout
components/CommandPalette/CommandPalette.tsx, components/UnifiedContentCard/UnifiedContentCard.tsx
Replaces CommandPalette's always-visible Esc hint with a desktop kbd hint plus mobile X-icon close button; adjusts UnifiedContentCard outer padding and author/byline truncation classes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • codu-code/codu#1324: Modifies _userLinkDetail.tsx, the same file where this PR adjusts responsive wrapper padding across loading, error, and main article states.
  • codu-code/codu#1322: This PR's sync-feeds/route.ts URL normalization directly extends the RSS sync ingest logic introduced in that PR.

Poem

🐰 Hop hop, the URLs are clean,
No doubled origins to be seen!
On mobile screens the padding shrinks,
A search icon button winks and blinks.
The drawer now greets guests with flair—
"Join free!" says the bunny with floppy ears! 🌟

✨ 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 fix/feed-images-and-mobile-layout

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 and usage tips.

NiallJoeMaherand others added 2 commits June 14, 2026 09:03
HackerNoon's RSS media:thumbnail/media:content URLs are malformed at the
source — an already-absolute CDN URL prefixed with their own origin
(https://hackernoon.com/https://cdn.hackernoon.com/…), which 404s. Our
ingestion stored them verbatim, and the redesigned cards SSR the <img>,
so the broken-image icon stuck: the error event fires before React
hydrates and attaches onError, so the fallback never runs.
- add unwrapDoubledUrl() (utils/url.ts) + unit tests
- card: unwrap at render and detect pre-hydration failures via a ref
callback, so a dead image collapses to no thumbnail (not a broken icon)
- sanitise URLs at ingestion (fetch-rss, admin/sync-feeds — media + OG)
- one-off scrub script for already-stored rows
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…reens
The relaunch redesign had several mobile regressions:
- cards overflowed the viewport (byline meta line couldn't shrink) — now
truncates; card padding p-4 on mobile
- app-main side gutters reduced to 0.75rem and per-page content wrappers
drop px-4 py-8 → px-0 py-4 on mobile (parent already gutters), so text
gets full reading width without double padding
- top bar collapses to burger + logo + search icon ≤720px; Log in / Join
free move into the nav drawer; real search SVG replaces the tiny glyph
- feed filters drop to their own row below the tabs instead of cramming
under them, and no longer sit in an overflow container that clipped the
FilterPill dropdowns (they now open on mobile)
- command palette: Esc hint becomes an X close button on mobile
- search field active state is a subtle accent underline, not the global
(blue-resolving) focus ring
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code review caught that the naive lastIndexOf scan would truncate a URL
whose path legitimately contains http://https:// deeper down (path-based
image proxies, or a slug literally containing a scheme). Match only an
absolute URL sitting immediately after the host — the exact shape of the
upstream doubled-origin bug — and leave everything else untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@NiallJoeMaher
NiallJoeMaherforce-pushed the fix/feed-images-and-mobile-layout branch from 3176a3c to cd2679dCompareJune 14, 2026 08:04
@NiallJoeMaher
NiallJoeMaher merged commit dd621f2 into developJun 14, 2026
3 of 5 checks passed
@NiallJoeMaher
NiallJoeMaher deleted the fix/feed-images-and-mobile-layout branch June 14, 2026 08:05
Sign up for freeto 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

@NiallJoeMaher
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: feed image thumbnails + mobile layout regressions - #1332

Merged
NiallJoeMaher merged 3 commits into
developfrom
fix/feed-images-and-mobile-layout
Jun 14, 2026
Merged

fix: feed image thumbnails + mobile layout regressions#1332
NiallJoeMaher merged 3 commits into
developfrom
fix/feed-images-and-mobile-layout

Conversation

@NiallJoeMaher

Copy link
Copy Markdown
Contributor

Follow-up fixes after the relaunch redesign merge.

1. Broken feed thumbnails (doubled image URLs)

HackerNoon's RSS media:thumbnail/media:content URLs are malformed at the source — an already-absolute CDN URL prefixed with their own origin (https://hackernoon.com/https://cdn.hackernoon.com/…), which 404s. Ingestion stored them verbatim, and because the redesigned cards SSR the <img>, the broken-image icon stuck (the error event fires before React hydrates and attaches onError).

  • unwrapDoubledUrl() helper in utils/url.ts (+ unit tests)
  • Card unwraps at render and detects pre-hydration failures via a ref callback → a dead image collapses to no thumbnail instead of a broken icon
  • Sanitised at ingestion (fetch-rss, admin/sync-feeds — media + OG image)
  • One-off scripts/fix-doubled-image-urls.ts to scrub already-stored rows (display is fixed without it; this cleans data server-side SEO/OG reads)

2. Mobile layout regressions

  • No horizontal overflow — byline meta truncates; card padding p-4 on mobile
  • Side gutters.app-main mobile gutter → 0.75rem; per-page content wrappers px-4 py-8px-0 py-4 on mobile (parent already gutters), so text gets full reading width
  • Top bar collapses to burger + logo + search icon ≤720px; Log in / Join free move into the nav drawer; real search SVG replaces the tiny glyph
  • Feed filters drop to their own row below the tabs and no longer sit in an overflow container that clipped the dropdowns (they now open on mobile)
  • Command palette Esc hint → X close button on mobile
  • Search field active state is a subtle accent underline, not the global (blue-resolving) focus ring

Verification

  • utils/url.test.ts passes (6/6); ESLint + tsc clean
  • Verified at 390px via Playwright: 0 horizontal overflow, 25 cards, top-bar/drawer/filter-dropdown/palette-close all confirmed

🤖 Generated with Claude Code

@NiallJoeMaher
NiallJoeMaher requested a review from a team as a code ownerJune 14, 2026 07:56
@vercel

vercelBot commented Jun 14, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
coduReadyReadyPreview, CommentJun 14, 2026 8:06am

Request Review

@coderabbitai

coderabbitaiBot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8dbbf420-a62e-4262-9d19-71429ff30111

📥 Commits

Reviewing files that changed from the base of the PR and between 4678f5a and cd2679d.

📒 Files selected for processing (25)
  • app/(app)/[username]/[slug]/_userLinkDetail.tsx
  • app/(app)/admin/_client.tsx
  • app/(app)/admin/moderation/_client.tsx
  • app/(app)/admin/sources/_client.tsx
  • app/(app)/admin/tags/_client.tsx
  • app/(app)/admin/users/_client.tsx
  • app/(app)/company/[slug]/page.tsx
  • app/(app)/draft/[id]/page.tsx
  • app/(app)/feed/_client.tsx
  • app/(app)/s/[sourceSlug]/[slug]/_feedArticleContent.tsx
  • app/(app)/s/[sourceSlug]/_sourceProfileClient.tsx
  • app/(app)/tag/[slug]/page.tsx
  • app/api/admin/sync-feeds/route.ts
  • components/CommandPalette/CommandPalette.tsx
  • components/ContentDetail/Layout.tsx
  • components/ContentDetail/PostReader.tsx
  • components/Feed/Filters.tsx
  • components/Layout/NavDrawer.tsx
  • components/Layout/TopBar.tsx
  • components/UnifiedContentCard/UnifiedContentCard.tsx
  • scripts/fetch-rss.ts
  • scripts/fix-doubled-image-urls.ts
  • styles/globals.css
  • utils/url.test.ts
  • utils/url.ts

Walkthrough

This PR adds an unwrapDoubledUrl URL-normalization utility and applies it across RSS sync, fetch-rss script, and UnifiedContentCard, plus a one-off database cleanup script. Separately, it updates responsive padding (px-0 py-4 sm:px-4 sm:py-8) across all page containers, refactors TopBar search into reusable internal components, adds auth buttons to NavDrawer for logged-out users, and improves CommandPalette's mobile close affordance.

Changes

Doubled image URL normalization

Layer / File(s)Summary
unwrapDoubledUrl utility and tests
utils/url.ts, utils/url.test.ts
Adds DOUBLED_ORIGIN regex and exported unwrapDoubledUrl function, covered by a Vitest suite including edge cases and composition with ensureHttps.
One-off database fix script
scripts/fix-doubled-image-urls.ts
New script scans posts.coverImage and aggregated_article.imageUrl/ogImageUrl for doubled-URL patterns, normalizes via unwrapDoubledUrl + ensureHttps, updates changed rows, and exits 0/1.
URL normalization wired into sync, fetch-rss, and content card
app/api/admin/sync-feeds/route.ts, scripts/fetch-rss.ts, components/UnifiedContentCard/UnifiedContentCard.tsx
Integrates unwrapDoubledUrl + ensureHttps into extractImageUrl, post-OG-fetch normalization, fetch-rss image derivation, and UnifiedContentCard image URL computation plus SSR hydration broken-image ref.

Mobile-responsive layout and navigation UX

Layer / File(s)Summary
Global CSS and page-level responsive padding
styles/globals.css, app/(app)/[username]/[slug]/_userLinkDetail.tsx, app/(app)/admin/_client.tsx, app/(app)/admin/moderation/_client.tsx, app/(app)/admin/sources/_client.tsx, app/(app)/admin/tags/_client.tsx, app/(app)/admin/users/_client.tsx, app/(app)/company/[slug]/page.tsx, app/(app)/draft/[id]/page.tsx, app/(app)/s/[sourceSlug]/[slug]/_feedArticleContent.tsx, app/(app)/s/[sourceSlug]/_sourceProfileClient.tsx, app/(app)/tag/[slug]/page.tsx
Updates .app-main mobile padding to a three-value shorthand in CSS, then applies px-0 py-4 sm:px-4 sm:py-8 to all page-level containers.
Shared ContentDetail and Feed layout
components/ContentDetail/Layout.tsx, components/ContentDetail/PostReader.tsx, components/Feed/Filters.tsx, app/(app)/feed/_client.tsx
Applies responsive padding to ContentDetailLayout and PostReader; updates FeedFilters to a wrapping right-justified flex layout and moves the signed-in filter cluster into a full-width mobile row.
TopBar search icon refactor and mobile auth hide
components/Layout/TopBar.tsx
Extracts SearchIcon SVG and SearchIconButton internal components, shows desktop search only at min-[721px], adds mobile SearchIconButton for both auth states, and hides login/join buttons on mobile.
NavDrawer mobile auth CTA buttons
components/Layout/NavDrawer.tsx
Adds "Join free" and "Log in" buttons inside NavDrawer for logged-out sessions; each button calls onClose() then signIn().
CommandPalette mobile close and content card layout
components/CommandPalette/CommandPalette.tsx, components/UnifiedContentCard/UnifiedContentCard.tsx
Replaces CommandPalette's always-visible Esc hint with a desktop kbd hint plus mobile X-icon close button; adjusts UnifiedContentCard outer padding and author/byline truncation classes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • codu-code/codu#1324: Modifies _userLinkDetail.tsx, the same file where this PR adjusts responsive wrapper padding across loading, error, and main article states.
  • codu-code/codu#1322: This PR's sync-feeds/route.ts URL normalization directly extends the RSS sync ingest logic introduced in that PR.

Poem

🐰 Hop hop, the URLs are clean,
No doubled origins to be seen!
On mobile screens the padding shrinks,
A search icon button winks and blinks.
The drawer now greets guests with flair—
"Join free!" says the bunny with floppy ears! 🌟

✨ 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 fix/feed-images-and-mobile-layout

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 and usage tips.

NiallJoeMaherand others added 2 commits June 14, 2026 09:03
HackerNoon's RSS media:thumbnail/media:content URLs are malformed at the
source — an already-absolute CDN URL prefixed with their own origin
(https://hackernoon.com/https://cdn.hackernoon.com/…), which 404s. Our
ingestion stored them verbatim, and the redesigned cards SSR the <img>,
so the broken-image icon stuck: the error event fires before React
hydrates and attaches onError, so the fallback never runs.
- add unwrapDoubledUrl() (utils/url.ts) + unit tests
- card: unwrap at render and detect pre-hydration failures via a ref
callback, so a dead image collapses to no thumbnail (not a broken icon)
- sanitise URLs at ingestion (fetch-rss, admin/sync-feeds — media + OG)
- one-off scrub script for already-stored rows
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…reens
The relaunch redesign had several mobile regressions:
- cards overflowed the viewport (byline meta line couldn't shrink) — now
truncates; card padding p-4 on mobile
- app-main side gutters reduced to 0.75rem and per-page content wrappers
drop px-4 py-8 → px-0 py-4 on mobile (parent already gutters), so text
gets full reading width without double padding
- top bar collapses to burger + logo + search icon ≤720px; Log in / Join
free move into the nav drawer; real search SVG replaces the tiny glyph
- feed filters drop to their own row below the tabs instead of cramming
under them, and no longer sit in an overflow container that clipped the
FilterPill dropdowns (they now open on mobile)
- command palette: Esc hint becomes an X close button on mobile
- search field active state is a subtle accent underline, not the global
(blue-resolving) focus ring
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code review caught that the naive lastIndexOf scan would truncate a URL
whose path legitimately contains http://https:// deeper down (path-based
image proxies, or a slug literally containing a scheme). Match only an
absolute URL sitting immediately after the host — the exact shape of the
upstream doubled-origin bug — and leave everything else untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@NiallJoeMaher
NiallJoeMaherforce-pushed the fix/feed-images-and-mobile-layout branch from 3176a3c to cd2679dCompareJune 14, 2026 08:04
@NiallJoeMaher
NiallJoeMaher merged commit dd621f2 into developJun 14, 2026
3 of 5 checks passed
@NiallJoeMaher
NiallJoeMaher deleted the fix/feed-images-and-mobile-layout branch June 14, 2026 08:05
Sign up for freeto 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

@NiallJoeMaher
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: feed image thumbnails + mobile layout regressions - #1332

Merged
NiallJoeMaher merged 3 commits into
developfrom
fix/feed-images-and-mobile-layout
Jun 14, 2026
Merged

fix: feed image thumbnails + mobile layout regressions#1332
NiallJoeMaher merged 3 commits into
developfrom
fix/feed-images-and-mobile-layout

Conversation

@NiallJoeMaher

Copy link
Copy Markdown
Contributor

Follow-up fixes after the relaunch redesign merge.

1. Broken feed thumbnails (doubled image URLs)

HackerNoon's RSS media:thumbnail/media:content URLs are malformed at the source — an already-absolute CDN URL prefixed with their own origin (https://hackernoon.com/https://cdn.hackernoon.com/…), which 404s. Ingestion stored them verbatim, and because the redesigned cards SSR the <img>, the broken-image icon stuck (the error event fires before React hydrates and attaches onError).

  • unwrapDoubledUrl() helper in utils/url.ts (+ unit tests)
  • Card unwraps at render and detects pre-hydration failures via a ref callback → a dead image collapses to no thumbnail instead of a broken icon
  • Sanitised at ingestion (fetch-rss, admin/sync-feeds — media + OG image)
  • One-off scripts/fix-doubled-image-urls.ts to scrub already-stored rows (display is fixed without it; this cleans data server-side SEO/OG reads)

2. Mobile layout regressions

  • No horizontal overflow — byline meta truncates; card padding p-4 on mobile
  • Side gutters.app-main mobile gutter → 0.75rem; per-page content wrappers px-4 py-8px-0 py-4 on mobile (parent already gutters), so text gets full reading width
  • Top bar collapses to burger + logo + search icon ≤720px; Log in / Join free move into the nav drawer; real search SVG replaces the tiny glyph
  • Feed filters drop to their own row below the tabs and no longer sit in an overflow container that clipped the dropdowns (they now open on mobile)
  • Command palette Esc hint → X close button on mobile
  • Search field active state is a subtle accent underline, not the global (blue-resolving) focus ring

Verification

  • utils/url.test.ts passes (6/6); ESLint + tsc clean
  • Verified at 390px via Playwright: 0 horizontal overflow, 25 cards, top-bar/drawer/filter-dropdown/palette-close all confirmed

🤖 Generated with Claude Code

@NiallJoeMaher
NiallJoeMaher requested a review from a team as a code ownerJune 14, 2026 07:56
@vercel

vercelBot commented Jun 14, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
coduReadyReadyPreview, CommentJun 14, 2026 8:06am

Request Review

@coderabbitai

coderabbitaiBot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8dbbf420-a62e-4262-9d19-71429ff30111

📥 Commits

Reviewing files that changed from the base of the PR and between 4678f5a and cd2679d.

📒 Files selected for processing (25)
  • app/(app)/[username]/[slug]/_userLinkDetail.tsx
  • app/(app)/admin/_client.tsx
  • app/(app)/admin/moderation/_client.tsx
  • app/(app)/admin/sources/_client.tsx
  • app/(app)/admin/tags/_client.tsx
  • app/(app)/admin/users/_client.tsx
  • app/(app)/company/[slug]/page.tsx
  • app/(app)/draft/[id]/page.tsx
  • app/(app)/feed/_client.tsx
  • app/(app)/s/[sourceSlug]/[slug]/_feedArticleContent.tsx
  • app/(app)/s/[sourceSlug]/_sourceProfileClient.tsx
  • app/(app)/tag/[slug]/page.tsx
  • app/api/admin/sync-feeds/route.ts
  • components/CommandPalette/CommandPalette.tsx
  • components/ContentDetail/Layout.tsx
  • components/ContentDetail/PostReader.tsx
  • components/Feed/Filters.tsx
  • components/Layout/NavDrawer.tsx
  • components/Layout/TopBar.tsx
  • components/UnifiedContentCard/UnifiedContentCard.tsx
  • scripts/fetch-rss.ts
  • scripts/fix-doubled-image-urls.ts
  • styles/globals.css
  • utils/url.test.ts
  • utils/url.ts

Walkthrough

This PR adds an unwrapDoubledUrl URL-normalization utility and applies it across RSS sync, fetch-rss script, and UnifiedContentCard, plus a one-off database cleanup script. Separately, it updates responsive padding (px-0 py-4 sm:px-4 sm:py-8) across all page containers, refactors TopBar search into reusable internal components, adds auth buttons to NavDrawer for logged-out users, and improves CommandPalette's mobile close affordance.

Changes

Doubled image URL normalization

Layer / File(s)Summary
unwrapDoubledUrl utility and tests
utils/url.ts, utils/url.test.ts
Adds DOUBLED_ORIGIN regex and exported unwrapDoubledUrl function, covered by a Vitest suite including edge cases and composition with ensureHttps.
One-off database fix script
scripts/fix-doubled-image-urls.ts
New script scans posts.coverImage and aggregated_article.imageUrl/ogImageUrl for doubled-URL patterns, normalizes via unwrapDoubledUrl + ensureHttps, updates changed rows, and exits 0/1.
URL normalization wired into sync, fetch-rss, and content card
app/api/admin/sync-feeds/route.ts, scripts/fetch-rss.ts, components/UnifiedContentCard/UnifiedContentCard.tsx
Integrates unwrapDoubledUrl + ensureHttps into extractImageUrl, post-OG-fetch normalization, fetch-rss image derivation, and UnifiedContentCard image URL computation plus SSR hydration broken-image ref.

Mobile-responsive layout and navigation UX

Layer / File(s)Summary
Global CSS and page-level responsive padding
styles/globals.css, app/(app)/[username]/[slug]/_userLinkDetail.tsx, app/(app)/admin/_client.tsx, app/(app)/admin/moderation/_client.tsx, app/(app)/admin/sources/_client.tsx, app/(app)/admin/tags/_client.tsx, app/(app)/admin/users/_client.tsx, app/(app)/company/[slug]/page.tsx, app/(app)/draft/[id]/page.tsx, app/(app)/s/[sourceSlug]/[slug]/_feedArticleContent.tsx, app/(app)/s/[sourceSlug]/_sourceProfileClient.tsx, app/(app)/tag/[slug]/page.tsx
Updates .app-main mobile padding to a three-value shorthand in CSS, then applies px-0 py-4 sm:px-4 sm:py-8 to all page-level containers.
Shared ContentDetail and Feed layout
components/ContentDetail/Layout.tsx, components/ContentDetail/PostReader.tsx, components/Feed/Filters.tsx, app/(app)/feed/_client.tsx
Applies responsive padding to ContentDetailLayout and PostReader; updates FeedFilters to a wrapping right-justified flex layout and moves the signed-in filter cluster into a full-width mobile row.
TopBar search icon refactor and mobile auth hide
components/Layout/TopBar.tsx
Extracts SearchIcon SVG and SearchIconButton internal components, shows desktop search only at min-[721px], adds mobile SearchIconButton for both auth states, and hides login/join buttons on mobile.
NavDrawer mobile auth CTA buttons
components/Layout/NavDrawer.tsx
Adds "Join free" and "Log in" buttons inside NavDrawer for logged-out sessions; each button calls onClose() then signIn().
CommandPalette mobile close and content card layout
components/CommandPalette/CommandPalette.tsx, components/UnifiedContentCard/UnifiedContentCard.tsx
Replaces CommandPalette's always-visible Esc hint with a desktop kbd hint plus mobile X-icon close button; adjusts UnifiedContentCard outer padding and author/byline truncation classes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • codu-code/codu#1324: Modifies _userLinkDetail.tsx, the same file where this PR adjusts responsive wrapper padding across loading, error, and main article states.
  • codu-code/codu#1322: This PR's sync-feeds/route.ts URL normalization directly extends the RSS sync ingest logic introduced in that PR.

Poem

🐰 Hop hop, the URLs are clean,
No doubled origins to be seen!
On mobile screens the padding shrinks,
A search icon button winks and blinks.
The drawer now greets guests with flair—
"Join free!" says the bunny with floppy ears! 🌟

✨ 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 fix/feed-images-and-mobile-layout

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 and usage tips.

NiallJoeMaherand others added 2 commits June 14, 2026 09:03
HackerNoon's RSS media:thumbnail/media:content URLs are malformed at the
source — an already-absolute CDN URL prefixed with their own origin
(https://hackernoon.com/https://cdn.hackernoon.com/…), which 404s. Our
ingestion stored them verbatim, and the redesigned cards SSR the <img>,
so the broken-image icon stuck: the error event fires before React
hydrates and attaches onError, so the fallback never runs.
- add unwrapDoubledUrl() (utils/url.ts) + unit tests
- card: unwrap at render and detect pre-hydration failures via a ref
callback, so a dead image collapses to no thumbnail (not a broken icon)
- sanitise URLs at ingestion (fetch-rss, admin/sync-feeds — media + OG)
- one-off scrub script for already-stored rows
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…reens
The relaunch redesign had several mobile regressions:
- cards overflowed the viewport (byline meta line couldn't shrink) — now
truncates; card padding p-4 on mobile
- app-main side gutters reduced to 0.75rem and per-page content wrappers
drop px-4 py-8 → px-0 py-4 on mobile (parent already gutters), so text
gets full reading width without double padding
- top bar collapses to burger + logo + search icon ≤720px; Log in / Join
free move into the nav drawer; real search SVG replaces the tiny glyph
- feed filters drop to their own row below the tabs instead of cramming
under them, and no longer sit in an overflow container that clipped the
FilterPill dropdowns (they now open on mobile)
- command palette: Esc hint becomes an X close button on mobile
- search field active state is a subtle accent underline, not the global
(blue-resolving) focus ring
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code review caught that the naive lastIndexOf scan would truncate a URL
whose path legitimately contains http://https:// deeper down (path-based
image proxies, or a slug literally containing a scheme). Match only an
absolute URL sitting immediately after the host — the exact shape of the
upstream doubled-origin bug — and leave everything else untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@NiallJoeMaher
NiallJoeMaherforce-pushed the fix/feed-images-and-mobile-layout branch from 3176a3c to cd2679dCompareJune 14, 2026 08:04
@NiallJoeMaher
NiallJoeMaher merged commit dd621f2 into developJun 14, 2026
3 of 5 checks passed
@NiallJoeMaher
NiallJoeMaher deleted the fix/feed-images-and-mobile-layout branch June 14, 2026 08:05
Sign up for freeto 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

@NiallJoeMaher
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix: feed image thumbnails + mobile layout regressions - #1332

Merged
NiallJoeMaher merged 3 commits into
developfrom
fix/feed-images-and-mobile-layout
Jun 14, 2026
Merged

fix: feed image thumbnails + mobile layout regressions#1332
NiallJoeMaher merged 3 commits into
developfrom
fix/feed-images-and-mobile-layout

Conversation

@NiallJoeMaher

Copy link
Copy Markdown
Contributor

Follow-up fixes after the relaunch redesign merge.

1. Broken feed thumbnails (doubled image URLs)

HackerNoon's RSS media:thumbnail/media:content URLs are malformed at the source — an already-absolute CDN URL prefixed with their own origin (https://hackernoon.com/https://cdn.hackernoon.com/…), which 404s. Ingestion stored them verbatim, and because the redesigned cards SSR the <img>, the broken-image icon stuck (the error event fires before React hydrates and attaches onError).

  • unwrapDoubledUrl() helper in utils/url.ts (+ unit tests)
  • Card unwraps at render and detects pre-hydration failures via a ref callback → a dead image collapses to no thumbnail instead of a broken icon
  • Sanitised at ingestion (fetch-rss, admin/sync-feeds — media + OG image)
  • One-off scripts/fix-doubled-image-urls.ts to scrub already-stored rows (display is fixed without it; this cleans data server-side SEO/OG reads)

2. Mobile layout regressions

  • No horizontal overflow — byline meta truncates; card padding p-4 on mobile
  • Side gutters.app-main mobile gutter → 0.75rem; per-page content wrappers px-4 py-8px-0 py-4 on mobile (parent already gutters), so text gets full reading width
  • Top bar collapses to burger + logo + search icon ≤720px; Log in / Join free move into the nav drawer; real search SVG replaces the tiny glyph
  • Feed filters drop to their own row below the tabs and no longer sit in an overflow container that clipped the dropdowns (they now open on mobile)
  • Command palette Esc hint → X close button on mobile
  • Search field active state is a subtle accent underline, not the global (blue-resolving) focus ring

Verification

  • utils/url.test.ts passes (6/6); ESLint + tsc clean
  • Verified at 390px via Playwright: 0 horizontal overflow, 25 cards, top-bar/drawer/filter-dropdown/palette-close all confirmed

🤖 Generated with Claude Code

@NiallJoeMaher
NiallJoeMaher requested a review from a team as a code ownerJune 14, 2026 07:56
@vercel

vercelBot commented Jun 14, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
coduReadyReadyPreview, CommentJun 14, 2026 8:06am

Request Review

@coderabbitai

coderabbitaiBot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8dbbf420-a62e-4262-9d19-71429ff30111

📥 Commits

Reviewing files that changed from the base of the PR and between 4678f5a and cd2679d.

📒 Files selected for processing (25)
  • app/(app)/[username]/[slug]/_userLinkDetail.tsx
  • app/(app)/admin/_client.tsx
  • app/(app)/admin/moderation/_client.tsx
  • app/(app)/admin/sources/_client.tsx
  • app/(app)/admin/tags/_client.tsx
  • app/(app)/admin/users/_client.tsx
  • app/(app)/company/[slug]/page.tsx
  • app/(app)/draft/[id]/page.tsx
  • app/(app)/feed/_client.tsx
  • app/(app)/s/[sourceSlug]/[slug]/_feedArticleContent.tsx
  • app/(app)/s/[sourceSlug]/_sourceProfileClient.tsx
  • app/(app)/tag/[slug]/page.tsx
  • app/api/admin/sync-feeds/route.ts
  • components/CommandPalette/CommandPalette.tsx
  • components/ContentDetail/Layout.tsx
  • components/ContentDetail/PostReader.tsx
  • components/Feed/Filters.tsx
  • components/Layout/NavDrawer.tsx
  • components/Layout/TopBar.tsx
  • components/UnifiedContentCard/UnifiedContentCard.tsx
  • scripts/fetch-rss.ts
  • scripts/fix-doubled-image-urls.ts
  • styles/globals.css
  • utils/url.test.ts
  • utils/url.ts

Walkthrough

This PR adds an unwrapDoubledUrl URL-normalization utility and applies it across RSS sync, fetch-rss script, and UnifiedContentCard, plus a one-off database cleanup script. Separately, it updates responsive padding (px-0 py-4 sm:px-4 sm:py-8) across all page containers, refactors TopBar search into reusable internal components, adds auth buttons to NavDrawer for logged-out users, and improves CommandPalette's mobile close affordance.

Changes

Doubled image URL normalization

Layer / File(s)Summary
unwrapDoubledUrl utility and tests
utils/url.ts, utils/url.test.ts
Adds DOUBLED_ORIGIN regex and exported unwrapDoubledUrl function, covered by a Vitest suite including edge cases and composition with ensureHttps.
One-off database fix script
scripts/fix-doubled-image-urls.ts
New script scans posts.coverImage and aggregated_article.imageUrl/ogImageUrl for doubled-URL patterns, normalizes via unwrapDoubledUrl + ensureHttps, updates changed rows, and exits 0/1.
URL normalization wired into sync, fetch-rss, and content card
app/api/admin/sync-feeds/route.ts, scripts/fetch-rss.ts, components/UnifiedContentCard/UnifiedContentCard.tsx
Integrates unwrapDoubledUrl + ensureHttps into extractImageUrl, post-OG-fetch normalization, fetch-rss image derivation, and UnifiedContentCard image URL computation plus SSR hydration broken-image ref.

Mobile-responsive layout and navigation UX

Layer / File(s)Summary
Global CSS and page-level responsive padding
styles/globals.css, app/(app)/[username]/[slug]/_userLinkDetail.tsx, app/(app)/admin/_client.tsx, app/(app)/admin/moderation/_client.tsx, app/(app)/admin/sources/_client.tsx, app/(app)/admin/tags/_client.tsx, app/(app)/admin/users/_client.tsx, app/(app)/company/[slug]/page.tsx, app/(app)/draft/[id]/page.tsx, app/(app)/s/[sourceSlug]/[slug]/_feedArticleContent.tsx, app/(app)/s/[sourceSlug]/_sourceProfileClient.tsx, app/(app)/tag/[slug]/page.tsx
Updates .app-main mobile padding to a three-value shorthand in CSS, then applies px-0 py-4 sm:px-4 sm:py-8 to all page-level containers.
Shared ContentDetail and Feed layout
components/ContentDetail/Layout.tsx, components/ContentDetail/PostReader.tsx, components/Feed/Filters.tsx, app/(app)/feed/_client.tsx
Applies responsive padding to ContentDetailLayout and PostReader; updates FeedFilters to a wrapping right-justified flex layout and moves the signed-in filter cluster into a full-width mobile row.
TopBar search icon refactor and mobile auth hide
components/Layout/TopBar.tsx
Extracts SearchIcon SVG and SearchIconButton internal components, shows desktop search only at min-[721px], adds mobile SearchIconButton for both auth states, and hides login/join buttons on mobile.
NavDrawer mobile auth CTA buttons
components/Layout/NavDrawer.tsx
Adds "Join free" and "Log in" buttons inside NavDrawer for logged-out sessions; each button calls onClose() then signIn().
CommandPalette mobile close and content card layout
components/CommandPalette/CommandPalette.tsx, components/UnifiedContentCard/UnifiedContentCard.tsx
Replaces CommandPalette's always-visible Esc hint with a desktop kbd hint plus mobile X-icon close button; adjusts UnifiedContentCard outer padding and author/byline truncation classes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • codu-code/codu#1324: Modifies _userLinkDetail.tsx, the same file where this PR adjusts responsive wrapper padding across loading, error, and main article states.
  • codu-code/codu#1322: This PR's sync-feeds/route.ts URL normalization directly extends the RSS sync ingest logic introduced in that PR.

Poem

🐰 Hop hop, the URLs are clean,
No doubled origins to be seen!
On mobile screens the padding shrinks,
A search icon button winks and blinks.
The drawer now greets guests with flair—
"Join free!" says the bunny with floppy ears! 🌟

✨ 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 fix/feed-images-and-mobile-layout

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 and usage tips.

NiallJoeMaherand others added 2 commits June 14, 2026 09:03
HackerNoon's RSS media:thumbnail/media:content URLs are malformed at the
source — an already-absolute CDN URL prefixed with their own origin
(https://hackernoon.com/https://cdn.hackernoon.com/…), which 404s. Our
ingestion stored them verbatim, and the redesigned cards SSR the <img>,
so the broken-image icon stuck: the error event fires before React
hydrates and attaches onError, so the fallback never runs.
- add unwrapDoubledUrl() (utils/url.ts) + unit tests
- card: unwrap at render and detect pre-hydration failures via a ref
callback, so a dead image collapses to no thumbnail (not a broken icon)
- sanitise URLs at ingestion (fetch-rss, admin/sync-feeds — media + OG)
- one-off scrub script for already-stored rows
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…reens
The relaunch redesign had several mobile regressions:
- cards overflowed the viewport (byline meta line couldn't shrink) — now
truncates; card padding p-4 on mobile
- app-main side gutters reduced to 0.75rem and per-page content wrappers
drop px-4 py-8 → px-0 py-4 on mobile (parent already gutters), so text
gets full reading width without double padding
- top bar collapses to burger + logo + search icon ≤720px; Log in / Join
free move into the nav drawer; real search SVG replaces the tiny glyph
- feed filters drop to their own row below the tabs instead of cramming
under them, and no longer sit in an overflow container that clipped the
FilterPill dropdowns (they now open on mobile)
- command palette: Esc hint becomes an X close button on mobile
- search field active state is a subtle accent underline, not the global
(blue-resolving) focus ring
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code review caught that the naive lastIndexOf scan would truncate a URL
whose path legitimately contains http://https:// deeper down (path-based
image proxies, or a slug literally containing a scheme). Match only an
absolute URL sitting immediately after the host — the exact shape of the
upstream doubled-origin bug — and leave everything else untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@NiallJoeMaher
NiallJoeMaherforce-pushed the fix/feed-images-and-mobile-layout branch from 3176a3c to cd2679dCompareJune 14, 2026 08:04
@NiallJoeMaher
NiallJoeMaher merged commit dd621f2 into developJun 14, 2026
3 of 5 checks passed
@NiallJoeMaher
NiallJoeMaher deleted the fix/feed-images-and-mobile-layout branch June 14, 2026 08:05
Sign up for freeto 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

@NiallJoeMaher
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: feed image thumbnails + mobile layout regressions - #1332

Merged
NiallJoeMaher merged 3 commits into
developfrom
fix/feed-images-and-mobile-layout
Jun 14, 2026
Merged

fix: feed image thumbnails + mobile layout regressions#1332
NiallJoeMaher merged 3 commits into
developfrom
fix/feed-images-and-mobile-layout

Conversation

@NiallJoeMaher

Copy link
Copy Markdown
Contributor

Follow-up fixes after the relaunch redesign merge.

1. Broken feed thumbnails (doubled image URLs)

HackerNoon's RSS media:thumbnail/media:content URLs are malformed at the source — an already-absolute CDN URL prefixed with their own origin (https://hackernoon.com/https://cdn.hackernoon.com/…), which 404s. Ingestion stored them verbatim, and because the redesigned cards SSR the <img>, the broken-image icon stuck (the error event fires before React hydrates and attaches onError).

  • unwrapDoubledUrl() helper in utils/url.ts (+ unit tests)
  • Card unwraps at render and detects pre-hydration failures via a ref callback → a dead image collapses to no thumbnail instead of a broken icon
  • Sanitised at ingestion (fetch-rss, admin/sync-feeds — media + OG image)
  • One-off scripts/fix-doubled-image-urls.ts to scrub already-stored rows (display is fixed without it; this cleans data server-side SEO/OG reads)

2. Mobile layout regressions

  • No horizontal overflow — byline meta truncates; card padding p-4 on mobile
  • Side gutters.app-main mobile gutter → 0.75rem; per-page content wrappers px-4 py-8px-0 py-4 on mobile (parent already gutters), so text gets full reading width
  • Top bar collapses to burger + logo + search icon ≤720px; Log in / Join free move into the nav drawer; real search SVG replaces the tiny glyph
  • Feed filters drop to their own row below the tabs and no longer sit in an overflow container that clipped the dropdowns (they now open on mobile)
  • Command palette Esc hint → X close button on mobile
  • Search field active state is a subtle accent underline, not the global (blue-resolving) focus ring

Verification

  • utils/url.test.ts passes (6/6); ESLint + tsc clean
  • Verified at 390px via Playwright: 0 horizontal overflow, 25 cards, top-bar/drawer/filter-dropdown/palette-close all confirmed

🤖 Generated with Claude Code

@NiallJoeMaher
NiallJoeMaher requested a review from a team as a code ownerJune 14, 2026 07:56
@vercel

vercelBot commented Jun 14, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
coduReadyReadyPreview, CommentJun 14, 2026 8:06am

Request Review

@coderabbitai

coderabbitaiBot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8dbbf420-a62e-4262-9d19-71429ff30111

📥 Commits

Reviewing files that changed from the base of the PR and between 4678f5a and cd2679d.

📒 Files selected for processing (25)
  • app/(app)/[username]/[slug]/_userLinkDetail.tsx
  • app/(app)/admin/_client.tsx
  • app/(app)/admin/moderation/_client.tsx
  • app/(app)/admin/sources/_client.tsx
  • app/(app)/admin/tags/_client.tsx
  • app/(app)/admin/users/_client.tsx
  • app/(app)/company/[slug]/page.tsx
  • app/(app)/draft/[id]/page.tsx
  • app/(app)/feed/_client.tsx
  • app/(app)/s/[sourceSlug]/[slug]/_feedArticleContent.tsx
  • app/(app)/s/[sourceSlug]/_sourceProfileClient.tsx
  • app/(app)/tag/[slug]/page.tsx
  • app/api/admin/sync-feeds/route.ts
  • components/CommandPalette/CommandPalette.tsx
  • components/ContentDetail/Layout.tsx
  • components/ContentDetail/PostReader.tsx
  • components/Feed/Filters.tsx
  • components/Layout/NavDrawer.tsx
  • components/Layout/TopBar.tsx
  • components/UnifiedContentCard/UnifiedContentCard.tsx
  • scripts/fetch-rss.ts
  • scripts/fix-doubled-image-urls.ts
  • styles/globals.css
  • utils/url.test.ts
  • utils/url.ts

Walkthrough

This PR adds an unwrapDoubledUrl URL-normalization utility and applies it across RSS sync, fetch-rss script, and UnifiedContentCard, plus a one-off database cleanup script. Separately, it updates responsive padding (px-0 py-4 sm:px-4 sm:py-8) across all page containers, refactors TopBar search into reusable internal components, adds auth buttons to NavDrawer for logged-out users, and improves CommandPalette's mobile close affordance.

Changes

Doubled image URL normalization

Layer / File(s)Summary
unwrapDoubledUrl utility and tests
utils/url.ts, utils/url.test.ts
Adds DOUBLED_ORIGIN regex and exported unwrapDoubledUrl function, covered by a Vitest suite including edge cases and composition with ensureHttps.
One-off database fix script
scripts/fix-doubled-image-urls.ts
New script scans posts.coverImage and aggregated_article.imageUrl/ogImageUrl for doubled-URL patterns, normalizes via unwrapDoubledUrl + ensureHttps, updates changed rows, and exits 0/1.
URL normalization wired into sync, fetch-rss, and content card
app/api/admin/sync-feeds/route.ts, scripts/fetch-rss.ts, components/UnifiedContentCard/UnifiedContentCard.tsx
Integrates unwrapDoubledUrl + ensureHttps into extractImageUrl, post-OG-fetch normalization, fetch-rss image derivation, and UnifiedContentCard image URL computation plus SSR hydration broken-image ref.

Mobile-responsive layout and navigation UX

Layer / File(s)Summary
Global CSS and page-level responsive padding
styles/globals.css, app/(app)/[username]/[slug]/_userLinkDetail.tsx, app/(app)/admin/_client.tsx, app/(app)/admin/moderation/_client.tsx, app/(app)/admin/sources/_client.tsx, app/(app)/admin/tags/_client.tsx, app/(app)/admin/users/_client.tsx, app/(app)/company/[slug]/page.tsx, app/(app)/draft/[id]/page.tsx, app/(app)/s/[sourceSlug]/[slug]/_feedArticleContent.tsx, app/(app)/s/[sourceSlug]/_sourceProfileClient.tsx, app/(app)/tag/[slug]/page.tsx
Updates .app-main mobile padding to a three-value shorthand in CSS, then applies px-0 py-4 sm:px-4 sm:py-8 to all page-level containers.
Shared ContentDetail and Feed layout
components/ContentDetail/Layout.tsx, components/ContentDetail/PostReader.tsx, components/Feed/Filters.tsx, app/(app)/feed/_client.tsx
Applies responsive padding to ContentDetailLayout and PostReader; updates FeedFilters to a wrapping right-justified flex layout and moves the signed-in filter cluster into a full-width mobile row.
TopBar search icon refactor and mobile auth hide
components/Layout/TopBar.tsx
Extracts SearchIcon SVG and SearchIconButton internal components, shows desktop search only at min-[721px], adds mobile SearchIconButton for both auth states, and hides login/join buttons on mobile.
NavDrawer mobile auth CTA buttons
components/Layout/NavDrawer.tsx
Adds "Join free" and "Log in" buttons inside NavDrawer for logged-out sessions; each button calls onClose() then signIn().
CommandPalette mobile close and content card layout
components/CommandPalette/CommandPalette.tsx, components/UnifiedContentCard/UnifiedContentCard.tsx
Replaces CommandPalette's always-visible Esc hint with a desktop kbd hint plus mobile X-icon close button; adjusts UnifiedContentCard outer padding and author/byline truncation classes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • codu-code/codu#1324: Modifies _userLinkDetail.tsx, the same file where this PR adjusts responsive wrapper padding across loading, error, and main article states.
  • codu-code/codu#1322: This PR's sync-feeds/route.ts URL normalization directly extends the RSS sync ingest logic introduced in that PR.

Poem

🐰 Hop hop, the URLs are clean,
No doubled origins to be seen!
On mobile screens the padding shrinks,
A search icon button winks and blinks.
The drawer now greets guests with flair—
"Join free!" says the bunny with floppy ears! 🌟

✨ 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 fix/feed-images-and-mobile-layout

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 and usage tips.

NiallJoeMaherand others added 2 commits June 14, 2026 09:03
HackerNoon's RSS media:thumbnail/media:content URLs are malformed at the
source — an already-absolute CDN URL prefixed with their own origin
(https://hackernoon.com/https://cdn.hackernoon.com/…), which 404s. Our
ingestion stored them verbatim, and the redesigned cards SSR the <img>,
so the broken-image icon stuck: the error event fires before React
hydrates and attaches onError, so the fallback never runs.
- add unwrapDoubledUrl() (utils/url.ts) + unit tests
- card: unwrap at render and detect pre-hydration failures via a ref
callback, so a dead image collapses to no thumbnail (not a broken icon)
- sanitise URLs at ingestion (fetch-rss, admin/sync-feeds — media + OG)
- one-off scrub script for already-stored rows
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…reens
The relaunch redesign had several mobile regressions:
- cards overflowed the viewport (byline meta line couldn't shrink) — now
truncates; card padding p-4 on mobile
- app-main side gutters reduced to 0.75rem and per-page content wrappers
drop px-4 py-8 → px-0 py-4 on mobile (parent already gutters), so text
gets full reading width without double padding
- top bar collapses to burger + logo + search icon ≤720px; Log in / Join
free move into the nav drawer; real search SVG replaces the tiny glyph
- feed filters drop to their own row below the tabs instead of cramming
under them, and no longer sit in an overflow container that clipped the
FilterPill dropdowns (they now open on mobile)
- command palette: Esc hint becomes an X close button on mobile
- search field active state is a subtle accent underline, not the global
(blue-resolving) focus ring
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code review caught that the naive lastIndexOf scan would truncate a URL
whose path legitimately contains http://https:// deeper down (path-based
image proxies, or a slug literally containing a scheme). Match only an
absolute URL sitting immediately after the host — the exact shape of the
upstream doubled-origin bug — and leave everything else untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@NiallJoeMaher
NiallJoeMaherforce-pushed the fix/feed-images-and-mobile-layout branch from 3176a3c to cd2679dCompareJune 14, 2026 08:04
@NiallJoeMaher
NiallJoeMaher merged commit dd621f2 into developJun 14, 2026
3 of 5 checks passed
@NiallJoeMaher
NiallJoeMaher deleted the fix/feed-images-and-mobile-layout branch June 14, 2026 08:05
Sign up for freeto 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

@NiallJoeMaher
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: feed image thumbnails + mobile layout regressions - #1332

Merged
NiallJoeMaher merged 3 commits into
developfrom
fix/feed-images-and-mobile-layout
Jun 14, 2026
Merged

fix: feed image thumbnails + mobile layout regressions#1332
NiallJoeMaher merged 3 commits into
developfrom
fix/feed-images-and-mobile-layout

Conversation

@NiallJoeMaher

Copy link
Copy Markdown
Contributor

Follow-up fixes after the relaunch redesign merge.

1. Broken feed thumbnails (doubled image URLs)

HackerNoon's RSS media:thumbnail/media:content URLs are malformed at the source — an already-absolute CDN URL prefixed with their own origin (https://hackernoon.com/https://cdn.hackernoon.com/…), which 404s. Ingestion stored them verbatim, and because the redesigned cards SSR the <img>, the broken-image icon stuck (the error event fires before React hydrates and attaches onError).

  • unwrapDoubledUrl() helper in utils/url.ts (+ unit tests)
  • Card unwraps at render and detects pre-hydration failures via a ref callback → a dead image collapses to no thumbnail instead of a broken icon
  • Sanitised at ingestion (fetch-rss, admin/sync-feeds — media + OG image)
  • One-off scripts/fix-doubled-image-urls.ts to scrub already-stored rows (display is fixed without it; this cleans data server-side SEO/OG reads)

2. Mobile layout regressions

  • No horizontal overflow — byline meta truncates; card padding p-4 on mobile
  • Side gutters.app-main mobile gutter → 0.75rem; per-page content wrappers px-4 py-8px-0 py-4 on mobile (parent already gutters), so text gets full reading width
  • Top bar collapses to burger + logo + search icon ≤720px; Log in / Join free move into the nav drawer; real search SVG replaces the tiny glyph
  • Feed filters drop to their own row below the tabs and no longer sit in an overflow container that clipped the dropdowns (they now open on mobile)
  • Command palette Esc hint → X close button on mobile
  • Search field active state is a subtle accent underline, not the global (blue-resolving) focus ring

Verification

  • utils/url.test.ts passes (6/6); ESLint + tsc clean
  • Verified at 390px via Playwright: 0 horizontal overflow, 25 cards, top-bar/drawer/filter-dropdown/palette-close all confirmed

🤖 Generated with Claude Code

@NiallJoeMaher
NiallJoeMaher requested a review from a team as a code ownerJune 14, 2026 07:56
@vercel

vercelBot commented Jun 14, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
coduReadyReadyPreview, CommentJun 14, 2026 8:06am

Request Review

@coderabbitai

coderabbitaiBot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8dbbf420-a62e-4262-9d19-71429ff30111

📥 Commits

Reviewing files that changed from the base of the PR and between 4678f5a and cd2679d.

📒 Files selected for processing (25)
  • app/(app)/[username]/[slug]/_userLinkDetail.tsx
  • app/(app)/admin/_client.tsx
  • app/(app)/admin/moderation/_client.tsx
  • app/(app)/admin/sources/_client.tsx
  • app/(app)/admin/tags/_client.tsx
  • app/(app)/admin/users/_client.tsx
  • app/(app)/company/[slug]/page.tsx
  • app/(app)/draft/[id]/page.tsx
  • app/(app)/feed/_client.tsx
  • app/(app)/s/[sourceSlug]/[slug]/_feedArticleContent.tsx
  • app/(app)/s/[sourceSlug]/_sourceProfileClient.tsx
  • app/(app)/tag/[slug]/page.tsx
  • app/api/admin/sync-feeds/route.ts
  • components/CommandPalette/CommandPalette.tsx
  • components/ContentDetail/Layout.tsx
  • components/ContentDetail/PostReader.tsx
  • components/Feed/Filters.tsx
  • components/Layout/NavDrawer.tsx
  • components/Layout/TopBar.tsx
  • components/UnifiedContentCard/UnifiedContentCard.tsx
  • scripts/fetch-rss.ts
  • scripts/fix-doubled-image-urls.ts
  • styles/globals.css
  • utils/url.test.ts
  • utils/url.ts

Walkthrough

This PR adds an unwrapDoubledUrl URL-normalization utility and applies it across RSS sync, fetch-rss script, and UnifiedContentCard, plus a one-off database cleanup script. Separately, it updates responsive padding (px-0 py-4 sm:px-4 sm:py-8) across all page containers, refactors TopBar search into reusable internal components, adds auth buttons to NavDrawer for logged-out users, and improves CommandPalette's mobile close affordance.

Changes

Doubled image URL normalization

Layer / File(s)Summary
unwrapDoubledUrl utility and tests
utils/url.ts, utils/url.test.ts
Adds DOUBLED_ORIGIN regex and exported unwrapDoubledUrl function, covered by a Vitest suite including edge cases and composition with ensureHttps.
One-off database fix script
scripts/fix-doubled-image-urls.ts
New script scans posts.coverImage and aggregated_article.imageUrl/ogImageUrl for doubled-URL patterns, normalizes via unwrapDoubledUrl + ensureHttps, updates changed rows, and exits 0/1.
URL normalization wired into sync, fetch-rss, and content card
app/api/admin/sync-feeds/route.ts, scripts/fetch-rss.ts, components/UnifiedContentCard/UnifiedContentCard.tsx
Integrates unwrapDoubledUrl + ensureHttps into extractImageUrl, post-OG-fetch normalization, fetch-rss image derivation, and UnifiedContentCard image URL computation plus SSR hydration broken-image ref.

Mobile-responsive layout and navigation UX

Layer / File(s)Summary
Global CSS and page-level responsive padding
styles/globals.css, app/(app)/[username]/[slug]/_userLinkDetail.tsx, app/(app)/admin/_client.tsx, app/(app)/admin/moderation/_client.tsx, app/(app)/admin/sources/_client.tsx, app/(app)/admin/tags/_client.tsx, app/(app)/admin/users/_client.tsx, app/(app)/company/[slug]/page.tsx, app/(app)/draft/[id]/page.tsx, app/(app)/s/[sourceSlug]/[slug]/_feedArticleContent.tsx, app/(app)/s/[sourceSlug]/_sourceProfileClient.tsx, app/(app)/tag/[slug]/page.tsx
Updates .app-main mobile padding to a three-value shorthand in CSS, then applies px-0 py-4 sm:px-4 sm:py-8 to all page-level containers.
Shared ContentDetail and Feed layout
components/ContentDetail/Layout.tsx, components/ContentDetail/PostReader.tsx, components/Feed/Filters.tsx, app/(app)/feed/_client.tsx
Applies responsive padding to ContentDetailLayout and PostReader; updates FeedFilters to a wrapping right-justified flex layout and moves the signed-in filter cluster into a full-width mobile row.
TopBar search icon refactor and mobile auth hide
components/Layout/TopBar.tsx
Extracts SearchIcon SVG and SearchIconButton internal components, shows desktop search only at min-[721px], adds mobile SearchIconButton for both auth states, and hides login/join buttons on mobile.
NavDrawer mobile auth CTA buttons
components/Layout/NavDrawer.tsx
Adds "Join free" and "Log in" buttons inside NavDrawer for logged-out sessions; each button calls onClose() then signIn().
CommandPalette mobile close and content card layout
components/CommandPalette/CommandPalette.tsx, components/UnifiedContentCard/UnifiedContentCard.tsx
Replaces CommandPalette's always-visible Esc hint with a desktop kbd hint plus mobile X-icon close button; adjusts UnifiedContentCard outer padding and author/byline truncation classes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • codu-code/codu#1324: Modifies _userLinkDetail.tsx, the same file where this PR adjusts responsive wrapper padding across loading, error, and main article states.
  • codu-code/codu#1322: This PR's sync-feeds/route.ts URL normalization directly extends the RSS sync ingest logic introduced in that PR.

Poem

🐰 Hop hop, the URLs are clean,
No doubled origins to be seen!
On mobile screens the padding shrinks,
A search icon button winks and blinks.
The drawer now greets guests with flair—
"Join free!" says the bunny with floppy ears! 🌟

✨ 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 fix/feed-images-and-mobile-layout

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 and usage tips.

NiallJoeMaherand others added 2 commits June 14, 2026 09:03
HackerNoon's RSS media:thumbnail/media:content URLs are malformed at the
source — an already-absolute CDN URL prefixed with their own origin
(https://hackernoon.com/https://cdn.hackernoon.com/…), which 404s. Our
ingestion stored them verbatim, and the redesigned cards SSR the <img>,
so the broken-image icon stuck: the error event fires before React
hydrates and attaches onError, so the fallback never runs.
- add unwrapDoubledUrl() (utils/url.ts) + unit tests
- card: unwrap at render and detect pre-hydration failures via a ref
callback, so a dead image collapses to no thumbnail (not a broken icon)
- sanitise URLs at ingestion (fetch-rss, admin/sync-feeds — media + OG)
- one-off scrub script for already-stored rows
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…reens
The relaunch redesign had several mobile regressions:
- cards overflowed the viewport (byline meta line couldn't shrink) — now
truncates; card padding p-4 on mobile
- app-main side gutters reduced to 0.75rem and per-page content wrappers
drop px-4 py-8 → px-0 py-4 on mobile (parent already gutters), so text
gets full reading width without double padding
- top bar collapses to burger + logo + search icon ≤720px; Log in / Join
free move into the nav drawer; real search SVG replaces the tiny glyph
- feed filters drop to their own row below the tabs instead of cramming
under them, and no longer sit in an overflow container that clipped the
FilterPill dropdowns (they now open on mobile)
- command palette: Esc hint becomes an X close button on mobile
- search field active state is a subtle accent underline, not the global
(blue-resolving) focus ring
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code review caught that the naive lastIndexOf scan would truncate a URL
whose path legitimately contains http://https:// deeper down (path-based
image proxies, or a slug literally containing a scheme). Match only an
absolute URL sitting immediately after the host — the exact shape of the
upstream doubled-origin bug — and leave everything else untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@NiallJoeMaher
NiallJoeMaherforce-pushed the fix/feed-images-and-mobile-layout branch from 3176a3c to cd2679dCompareJune 14, 2026 08:04
@NiallJoeMaher
NiallJoeMaher merged commit dd621f2 into developJun 14, 2026
3 of 5 checks passed
@NiallJoeMaher
NiallJoeMaher deleted the fix/feed-images-and-mobile-layout branch June 14, 2026 08:05
Sign up for freeto 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

@NiallJoeMaher
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix: feed image thumbnails + mobile layout regressions - #1332

Merged
NiallJoeMaher merged 3 commits into
developfrom
fix/feed-images-and-mobile-layout
Jun 14, 2026
Merged

fix: feed image thumbnails + mobile layout regressions#1332
NiallJoeMaher merged 3 commits into
developfrom
fix/feed-images-and-mobile-layout

Conversation

@NiallJoeMaher

Copy link
Copy Markdown
Contributor

Follow-up fixes after the relaunch redesign merge.

1. Broken feed thumbnails (doubled image URLs)

HackerNoon's RSS media:thumbnail/media:content URLs are malformed at the source — an already-absolute CDN URL prefixed with their own origin (https://hackernoon.com/https://cdn.hackernoon.com/…), which 404s. Ingestion stored them verbatim, and because the redesigned cards SSR the <img>, the broken-image icon stuck (the error event fires before React hydrates and attaches onError).

  • unwrapDoubledUrl() helper in utils/url.ts (+ unit tests)
  • Card unwraps at render and detects pre-hydration failures via a ref callback → a dead image collapses to no thumbnail instead of a broken icon
  • Sanitised at ingestion (fetch-rss, admin/sync-feeds — media + OG image)
  • One-off scripts/fix-doubled-image-urls.ts to scrub already-stored rows (display is fixed without it; this cleans data server-side SEO/OG reads)

2. Mobile layout regressions

  • No horizontal overflow — byline meta truncates; card padding p-4 on mobile
  • Side gutters.app-main mobile gutter → 0.75rem; per-page content wrappers px-4 py-8px-0 py-4 on mobile (parent already gutters), so text gets full reading width
  • Top bar collapses to burger + logo + search icon ≤720px; Log in / Join free move into the nav drawer; real search SVG replaces the tiny glyph
  • Feed filters drop to their own row below the tabs and no longer sit in an overflow container that clipped the dropdowns (they now open on mobile)
  • Command palette Esc hint → X close button on mobile
  • Search field active state is a subtle accent underline, not the global (blue-resolving) focus ring

Verification

  • utils/url.test.ts passes (6/6); ESLint + tsc clean
  • Verified at 390px via Playwright: 0 horizontal overflow, 25 cards, top-bar/drawer/filter-dropdown/palette-close all confirmed

🤖 Generated with Claude Code

@NiallJoeMaher
NiallJoeMaher requested a review from a team as a code ownerJune 14, 2026 07:56
@vercel

vercelBot commented Jun 14, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
coduReadyReadyPreview, CommentJun 14, 2026 8:06am

Request Review

@coderabbitai

coderabbitaiBot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8dbbf420-a62e-4262-9d19-71429ff30111

📥 Commits

Reviewing files that changed from the base of the PR and between 4678f5a and cd2679d.

📒 Files selected for processing (25)
  • app/(app)/[username]/[slug]/_userLinkDetail.tsx
  • app/(app)/admin/_client.tsx
  • app/(app)/admin/moderation/_client.tsx
  • app/(app)/admin/sources/_client.tsx
  • app/(app)/admin/tags/_client.tsx
  • app/(app)/admin/users/_client.tsx
  • app/(app)/company/[slug]/page.tsx
  • app/(app)/draft/[id]/page.tsx
  • app/(app)/feed/_client.tsx
  • app/(app)/s/[sourceSlug]/[slug]/_feedArticleContent.tsx
  • app/(app)/s/[sourceSlug]/_sourceProfileClient.tsx
  • app/(app)/tag/[slug]/page.tsx
  • app/api/admin/sync-feeds/route.ts
  • components/CommandPalette/CommandPalette.tsx
  • components/ContentDetail/Layout.tsx
  • components/ContentDetail/PostReader.tsx
  • components/Feed/Filters.tsx
  • components/Layout/NavDrawer.tsx
  • components/Layout/TopBar.tsx
  • components/UnifiedContentCard/UnifiedContentCard.tsx
  • scripts/fetch-rss.ts
  • scripts/fix-doubled-image-urls.ts
  • styles/globals.css
  • utils/url.test.ts
  • utils/url.ts

Walkthrough

This PR adds an unwrapDoubledUrl URL-normalization utility and applies it across RSS sync, fetch-rss script, and UnifiedContentCard, plus a one-off database cleanup script. Separately, it updates responsive padding (px-0 py-4 sm:px-4 sm:py-8) across all page containers, refactors TopBar search into reusable internal components, adds auth buttons to NavDrawer for logged-out users, and improves CommandPalette's mobile close affordance.

Changes

Doubled image URL normalization

Layer / File(s)Summary
unwrapDoubledUrl utility and tests
utils/url.ts, utils/url.test.ts
Adds DOUBLED_ORIGIN regex and exported unwrapDoubledUrl function, covered by a Vitest suite including edge cases and composition with ensureHttps.
One-off database fix script
scripts/fix-doubled-image-urls.ts
New script scans posts.coverImage and aggregated_article.imageUrl/ogImageUrl for doubled-URL patterns, normalizes via unwrapDoubledUrl + ensureHttps, updates changed rows, and exits 0/1.
URL normalization wired into sync, fetch-rss, and content card
app/api/admin/sync-feeds/route.ts, scripts/fetch-rss.ts, components/UnifiedContentCard/UnifiedContentCard.tsx
Integrates unwrapDoubledUrl + ensureHttps into extractImageUrl, post-OG-fetch normalization, fetch-rss image derivation, and UnifiedContentCard image URL computation plus SSR hydration broken-image ref.

Mobile-responsive layout and navigation UX

Layer / File(s)Summary
Global CSS and page-level responsive padding
styles/globals.css, app/(app)/[username]/[slug]/_userLinkDetail.tsx, app/(app)/admin/_client.tsx, app/(app)/admin/moderation/_client.tsx, app/(app)/admin/sources/_client.tsx, app/(app)/admin/tags/_client.tsx, app/(app)/admin/users/_client.tsx, app/(app)/company/[slug]/page.tsx, app/(app)/draft/[id]/page.tsx, app/(app)/s/[sourceSlug]/[slug]/_feedArticleContent.tsx, app/(app)/s/[sourceSlug]/_sourceProfileClient.tsx, app/(app)/tag/[slug]/page.tsx
Updates .app-main mobile padding to a three-value shorthand in CSS, then applies px-0 py-4 sm:px-4 sm:py-8 to all page-level containers.
Shared ContentDetail and Feed layout
components/ContentDetail/Layout.tsx, components/ContentDetail/PostReader.tsx, components/Feed/Filters.tsx, app/(app)/feed/_client.tsx
Applies responsive padding to ContentDetailLayout and PostReader; updates FeedFilters to a wrapping right-justified flex layout and moves the signed-in filter cluster into a full-width mobile row.
TopBar search icon refactor and mobile auth hide
components/Layout/TopBar.tsx
Extracts SearchIcon SVG and SearchIconButton internal components, shows desktop search only at min-[721px], adds mobile SearchIconButton for both auth states, and hides login/join buttons on mobile.
NavDrawer mobile auth CTA buttons
components/Layout/NavDrawer.tsx
Adds "Join free" and "Log in" buttons inside NavDrawer for logged-out sessions; each button calls onClose() then signIn().
CommandPalette mobile close and content card layout
components/CommandPalette/CommandPalette.tsx, components/UnifiedContentCard/UnifiedContentCard.tsx
Replaces CommandPalette's always-visible Esc hint with a desktop kbd hint plus mobile X-icon close button; adjusts UnifiedContentCard outer padding and author/byline truncation classes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • codu-code/codu#1324: Modifies _userLinkDetail.tsx, the same file where this PR adjusts responsive wrapper padding across loading, error, and main article states.
  • codu-code/codu#1322: This PR's sync-feeds/route.ts URL normalization directly extends the RSS sync ingest logic introduced in that PR.

Poem

🐰 Hop hop, the URLs are clean,
No doubled origins to be seen!
On mobile screens the padding shrinks,
A search icon button winks and blinks.
The drawer now greets guests with flair—
"Join free!" says the bunny with floppy ears! 🌟

✨ 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 fix/feed-images-and-mobile-layout

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 and usage tips.

NiallJoeMaherand others added 2 commits June 14, 2026 09:03
HackerNoon's RSS media:thumbnail/media:content URLs are malformed at the
source — an already-absolute CDN URL prefixed with their own origin
(https://hackernoon.com/https://cdn.hackernoon.com/…), which 404s. Our
ingestion stored them verbatim, and the redesigned cards SSR the <img>,
so the broken-image icon stuck: the error event fires before React
hydrates and attaches onError, so the fallback never runs.
- add unwrapDoubledUrl() (utils/url.ts) + unit tests
- card: unwrap at render and detect pre-hydration failures via a ref
callback, so a dead image collapses to no thumbnail (not a broken icon)
- sanitise URLs at ingestion (fetch-rss, admin/sync-feeds — media + OG)
- one-off scrub script for already-stored rows
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…reens
The relaunch redesign had several mobile regressions:
- cards overflowed the viewport (byline meta line couldn't shrink) — now
truncates; card padding p-4 on mobile
- app-main side gutters reduced to 0.75rem and per-page content wrappers
drop px-4 py-8 → px-0 py-4 on mobile (parent already gutters), so text
gets full reading width without double padding
- top bar collapses to burger + logo + search icon ≤720px; Log in / Join
free move into the nav drawer; real search SVG replaces the tiny glyph
- feed filters drop to their own row below the tabs instead of cramming
under them, and no longer sit in an overflow container that clipped the
FilterPill dropdowns (they now open on mobile)
- command palette: Esc hint becomes an X close button on mobile
- search field active state is a subtle accent underline, not the global
(blue-resolving) focus ring
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code review caught that the naive lastIndexOf scan would truncate a URL
whose path legitimately contains http://https:// deeper down (path-based
image proxies, or a slug literally containing a scheme). Match only an
absolute URL sitting immediately after the host — the exact shape of the
upstream doubled-origin bug — and leave everything else untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@NiallJoeMaher
NiallJoeMaherforce-pushed the fix/feed-images-and-mobile-layout branch from 3176a3c to cd2679dCompareJune 14, 2026 08:04
@NiallJoeMaher
NiallJoeMaher merged commit dd621f2 into developJun 14, 2026
3 of 5 checks passed
@NiallJoeMaher
NiallJoeMaher deleted the fix/feed-images-and-mobile-layout branch June 14, 2026 08:05
Sign up for freeto 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

@NiallJoeMaher