Substack Articles
** * RSS-based Substack embed (client-side) * - Finds elements matching `.substack-feed-embed` or `#substack-feed-embed` * - Pulls configuration from data-* attributes with sensible defaults * - Fetches the Substack RSS (via rss2json) and renders simple cards * * Optional global defaults: * window.SubstackFeedWidget = { substackUrl: '', posts: 3, showImages: true, showDates: true }; */ (function () { const globalDefaults = (window.SubstackFeedWidget || {}); const defaults = { substackUrl: '', posts: 3, showImages: true, showDates: true, ...globalDefaults }; const containers = document.querySelectorAll('.substack-feed-embed, #substack-feed-embed'); if (!containers.length) return; const fmtDate = (iso) => { try { // Use reader’s locale; tweak if you want a fixed one return new Intl.DateTimeFormat(undefined, { year: 'numeric', month: 'short', day: '2-digit' }).format(new Date(iso)); } catch { return ''; } }; const normalizeUrl = (u) => { if (!u) return ''; return /^https?:\/\//i.test(u) ? u : `https://${u}`; }; const extractFirstImg = (html) => { if (!html) return null; const m = html.match(/Error: No Substack URL specified.
'; return; } const feedBase = normalizeUrl(cfg.substackUrl); const rssUrl = `https://api.rss2json.com/v1/api.json?rss_url=${encodeURIComponent(`${feedBase}/feed`)}`; // Minimal skeleton while loading (optional) container.innerHTML = 'Loading…
'; try { const res = await fetchWithTimeout(rssUrl, 12000); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); if (data.status !== 'ok' || !Array.isArray(data.items)) { throw new Error(data.message || 'Failed to parse feed'); } const posts = data.items.slice(0, Math.max(1, cfg.posts)); if (!posts.length) { container.innerHTML = 'No posts available.
'; return; } // Clear and render container.innerHTML = ''; posts.forEach((post) => { container.appendChild(buildCard(post, cfg)); }); } catch (err) { console.error('Substack feed error:', err); container.innerHTML = 'Could not load posts. Please try again later.
'; } }); })();