From 1aa1ab8796f8b59d9509ea9d44016a4746903817 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 24 May 2026 22:39:07 +0000 Subject: [PATCH 1/3] feat: improve RSS section UX and performance Agent-Logs-Url: https://github.com/jaypatrick/jk.com/sessions/d286583f-3e9d-44b5-8d2b-85b652921fe4 Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com> --- src/components/sections/RssFeed.svelte | 26 +++++++++++++++++++------ src/components/sections/RssFeed.test.ts | 7 ++++--- src/pages/index.astro | 2 +- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/components/sections/RssFeed.svelte b/src/components/sections/RssFeed.svelte index ec83f80..97e9bef 100644 --- a/src/components/sections/RssFeed.svelte +++ b/src/components/sections/RssFeed.svelte @@ -14,16 +14,17 @@ let items = $state([]); let loading = $state(true); let error = $state(''); + const dateFormatter = new Intl.DateTimeFormat(undefined, { + year: 'numeric', + month: 'short', + day: 'numeric', + }); const formattedItems = $derived( items.map((item) => ({ ...item, formattedDate: item.pubDate - ? new Date(item.pubDate).toLocaleDateString(undefined, { - year: 'numeric', - month: 'short', - day: 'numeric', - }) + ? dateFormatter.format(new Date(item.pubDate)) : 'Date unavailable', })) ); @@ -33,6 +34,7 @@ maxItems; let cancelled = false; + const abortController = new AbortController(); loading = true; error = ''; items = []; @@ -45,7 +47,7 @@ params.set('max', String(maxItems)); try { - const response = await fetch(`/api/rss?${params.toString()}`); + const response = await fetch(`/api/rss?${params.toString()}`, { signal: abortController.signal }); const payload = (await response.json()) as { items?: FeedItem[]; error?: string }; if (!response.ok) { @@ -56,6 +58,9 @@ items = payload.items ?? []; } } catch (err) { + if (err instanceof DOMException && err.name === 'AbortError') { + return; + } console.error('[RssFeed] Failed to load feed:', err); if (!cancelled) { error = err instanceof Error ? err.message : 'Failed to load feed.'; @@ -71,6 +76,7 @@ return () => { cancelled = true; + abortController.abort(); }; }); @@ -227,6 +233,14 @@ color: var(--color-cyan, #00d4ff); } + .feed-title-link:focus-visible, + .feed-read-more:focus-visible, + .view-all-link:focus-visible { + outline: 2px solid var(--color-cyan, #00d4ff); + outline-offset: 2px; + border-radius: 2px; + } + /* ── Description ────────────────────────────────────── */ .feed-desc { font-size: 0.875rem; diff --git a/src/components/sections/RssFeed.test.ts b/src/components/sections/RssFeed.test.ts index 07a4046..6757701 100644 --- a/src/components/sections/RssFeed.test.ts +++ b/src/components/sections/RssFeed.test.ts @@ -4,6 +4,7 @@ import { fileURLToPath } from 'node:url'; const rssFeedPath = fileURLToPath(new URL('./RssFeed.svelte', import.meta.url)); const rssFeedSource = readFileSync(rssFeedPath, 'utf8'); +const feedCardBlock = rssFeedSource.match(/\.feed-card \{[\s\S]*?\}/)?.[0] ?? ''; describe('RssFeed section', () => { it('uses BootLabel with "WHAT I THINK" label', () => { @@ -35,9 +36,9 @@ describe('RssFeed section', () => { expect(rssFeedSource).toContain('transition: transform 0.2s ease;'); expect(rssFeedSource).toContain('.feed-card:hover {'); expect(rssFeedSource).toContain('transform: translateY(-3px);'); - expect(rssFeedSource).not.toContain('border:'); - expect(rssFeedSource).not.toContain('box-shadow:'); - expect(rssFeedSource).not.toContain('border-color:'); + expect(feedCardBlock).not.toContain('border:'); + expect(feedCardBlock).not.toContain('box-shadow:'); + expect(feedCardBlock).not.toContain('border-color:'); }); it('uses Svelte fade transition on feed item reveal', () => { diff --git a/src/pages/index.astro b/src/pages/index.astro index 3c09c8d..b3e2825 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -31,7 +31,7 @@ const { runtime } = Astro.locals; - + From 118a7bca508c968f2d05bf1710e3a574a073ac21 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 24 May 2026 22:42:00 +0000 Subject: [PATCH 2/3] test: align RSS tests with scoped style checks and date formatter Agent-Logs-Url: https://github.com/jaypatrick/jk.com/sessions/d286583f-3e9d-44b5-8d2b-85b652921fe4 Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com> --- src/components/sections/RssFeed.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/sections/RssFeed.test.ts b/src/components/sections/RssFeed.test.ts index 6757701..11a91f0 100644 --- a/src/components/sections/RssFeed.test.ts +++ b/src/components/sections/RssFeed.test.ts @@ -48,7 +48,8 @@ describe('RssFeed section', () => { it('derives formatted date strings from raw pubDate', () => { expect(rssFeedSource).toContain('formattedDate'); - expect(rssFeedSource).toContain('toLocaleDateString'); + expect(rssFeedSource).toContain('new Intl.DateTimeFormat'); + expect(rssFeedSource).toContain('dateFormatter.format(new Date(item.pubDate))'); expect(rssFeedSource).toContain("'Date unavailable'"); }); From 58638b4d74717333bdcccb2206fe177f85dfaa19 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 24 May 2026 23:23:41 +0000 Subject: [PATCH 3/3] Fix RSS review feedback for date parsing and test robustness Agent-Logs-Url: https://github.com/jaypatrick/jk.com/sessions/adafa504-1823-44db-b4a7-835d3eaf15c7 Co-authored-by: jaypatrick <1800595+jaypatrick@users.noreply.github.com> --- src/components/sections/RssFeed.svelte | 16 +++++++++++++--- src/components/sections/RssFeed.test.ts | 7 +++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/components/sections/RssFeed.svelte b/src/components/sections/RssFeed.svelte index 97e9bef..f224f7d 100644 --- a/src/components/sections/RssFeed.svelte +++ b/src/components/sections/RssFeed.svelte @@ -19,13 +19,23 @@ month: 'short', day: 'numeric', }); + const formatDate = (pubDate: string) => { + if (!pubDate) { + return 'Date unavailable'; + } + + const parsedDate = new Date(pubDate); + if (Number.isNaN(parsedDate.getTime())) { + return 'Date unavailable'; + } + + return dateFormatter.format(parsedDate); + }; const formattedItems = $derived( items.map((item) => ({ ...item, - formattedDate: item.pubDate - ? dateFormatter.format(new Date(item.pubDate)) - : 'Date unavailable', + formattedDate: formatDate(item.pubDate), })) ); diff --git a/src/components/sections/RssFeed.test.ts b/src/components/sections/RssFeed.test.ts index 11a91f0..61623c3 100644 --- a/src/components/sections/RssFeed.test.ts +++ b/src/components/sections/RssFeed.test.ts @@ -4,7 +4,8 @@ import { fileURLToPath } from 'node:url'; const rssFeedPath = fileURLToPath(new URL('./RssFeed.svelte', import.meta.url)); const rssFeedSource = readFileSync(rssFeedPath, 'utf8'); -const feedCardBlock = rssFeedSource.match(/\.feed-card \{[\s\S]*?\}/)?.[0] ?? ''; +const feedCardBlockMatch = rssFeedSource.match(/\.feed-card \{[\s\S]*?\}/); +const feedCardBlock = feedCardBlockMatch?.[0] ?? ''; describe('RssFeed section', () => { it('uses BootLabel with "WHAT I THINK" label', () => { @@ -29,6 +30,7 @@ describe('RssFeed section', () => { }); it('keeps only local layout and transform styles on feed-card so shared Phosphor utilities own the border glow', () => { + expect(feedCardBlockMatch).not.toBeNull(); expect(rssFeedSource).toContain('.feed-card {'); expect(rssFeedSource).toContain('position: relative;'); expect(rssFeedSource).toContain('background: var(--color-card, #111827);'); @@ -49,7 +51,8 @@ describe('RssFeed section', () => { it('derives formatted date strings from raw pubDate', () => { expect(rssFeedSource).toContain('formattedDate'); expect(rssFeedSource).toContain('new Intl.DateTimeFormat'); - expect(rssFeedSource).toContain('dateFormatter.format(new Date(item.pubDate))'); + expect(rssFeedSource).toContain('Number.isNaN(parsedDate.getTime())'); + expect(rssFeedSource).toContain('dateFormatter.format(parsedDate)'); expect(rssFeedSource).toContain("'Date unavailable'"); });