A calm place to write long-form, and publish it to the open social web. skypress.blog/
0

Configure Feed

Select the types of activity you want to include in your feed.

Reader: resolve + render embeds on the article page

+97 -2
+10 -2
src/pages/[author]/[slug]/[rkey].astro
··· 4 4 import { resolveArticleContext } from '../../../lib/reader/read-context'; 5 5 import { formatLongDate } from '../../../lib/reader/dates'; 6 6 import { renderArticle } from '../../../lib/reader/render-article'; 7 + import { resolveEmbeds } from '../../../lib/embeds/resolve'; 7 8 import { canonicalArticleUrl } from '../../../lib/publish/records'; 8 9 import { deriveExcerpt } from '../../../lib/publish/excerpt'; 9 10 import { themeStyleBlock } from '../../../lib/publish/themes'; ··· 21 22 import '../../../styles/post-actions.css'; 22 23 // "View record JSON" control + modal (a client:only island; chrome styled globally). 23 24 import '../../../styles/record-json.css'; 25 + // Embed cards (atproto posts) + video facade — frontend only. 26 + import '../../../styles/embeds.css'; 24 27 25 28 // Read-through renderer: resolve + fetch at request time (Decision 0007). 26 29 export const prerender = false; ··· 63 66 handle = readAuthor.handle; 64 67 65 68 const doc = document.value; 66 - // Blob URLs, render, sanitise, and the text fallback all live in render-article. 67 - const rendered = renderArticle( doc, { pdsUrl, did }, { highlight: true } ); 69 + // Resolve embeds (atproto cards + video facades) before rendering — async 70 + // fetches can't run inside the synchronous render-article pipeline. The 71 + // enriched blocks then flow through blob-resolve → render → sanitise. 72 + const blocksWithEmbeds = await resolveEmbeds( doc.content?.blocks ?? [] ); 73 + const docWithEmbeds = { ...doc, content: { ...doc.content, blocks: blocksWithEmbeds } }; 74 + const rendered = renderArticle( docWithEmbeds, { pdsUrl, did }, { highlight: true } ); 68 75 html = rendered.html; 69 76 const textContent = rendered.text; 70 77 // The PDS record body (just `value`) as highlighted, sanitised JSON for the viewer. ··· 161 168 </p> 162 169 <h1 class="reader__title">{title}</h1> 163 170 <article class="reader__article" set:html={html} /> 171 + <script src="../../../scripts/embed-facade.ts"></script> 164 172 {bskyPostRef && ( 165 173 <PostActions 166 174 client:only="react"
+87
src/styles/embeds.css
··· 1 + /* src/styles/embeds.css — embed cards on the reading page. Utility surfaces use 2 + fixed colors (not theme tokens) so per-publication themes don't recolor them. */ 3 + .skypress-embed { 4 + margin: 1.8rem 0; 5 + border: 1px solid var(--line); 6 + border-radius: var(--radius); 7 + overflow: hidden; 8 + background: var(--paper-raised); 9 + } 10 + .skypress-embed--atproto .skypress-embed__link { 11 + display: block; 12 + padding: 1rem 1.2rem; 13 + text-decoration: none; 14 + color: var(--ink); 15 + } 16 + .skypress-embed__head { 17 + display: flex; 18 + align-items: center; 19 + gap: 0.5rem; 20 + margin-bottom: 0.5rem; 21 + } 22 + .skypress-embed__avatar { 23 + width: 32px; 24 + height: 32px; 25 + border-radius: 50%; 26 + object-fit: cover; 27 + } 28 + .skypress-embed__author { font-weight: 600; } 29 + .skypress-embed__handle { color: var(--muted); font-size: 0.92rem; } 30 + .skypress-embed__text { 31 + display: block; 32 + white-space: pre-wrap; 33 + line-height: 1.5; 34 + } 35 + .skypress-embed__media { 36 + display: grid; 37 + gap: 0.4rem; 38 + margin-top: 0.75rem; 39 + } 40 + .skypress-embed__image { width: 100%; border-radius: 8px; } 41 + .skypress-embed__footer { 42 + display: block; 43 + margin-top: 0.75rem; 44 + font-size: 0.88rem; 45 + color: var(--muted); 46 + } 47 + /* Video facade */ 48 + .skypress-embed--video { position: relative; } 49 + .skypress-embed__play { 50 + display: block; 51 + width: 100%; 52 + padding: 0; 53 + border: 0; 54 + background: #000; 55 + cursor: pointer; 56 + position: relative; 57 + } 58 + .skypress-embed__thumb { width: 100%; display: block; opacity: 0.92; } 59 + .skypress-embed__playicon { 60 + position: absolute; 61 + inset: 0; 62 + display: flex; 63 + align-items: center; 64 + justify-content: center; 65 + font-size: 3rem; 66 + color: #fff; 67 + text-shadow: 0 2px 8px rgba( 0, 0, 0, 0.5 ); 68 + } 69 + .skypress-embed__title { 70 + display: block; 71 + padding: 0.6rem 1rem; 72 + color: #fff; 73 + background: #000; 74 + text-align: left; 75 + font-size: 0.95rem; 76 + } 77 + .skypress-embed__fallback { 78 + display: block; 79 + padding: 0.5rem 1rem; 80 + font-size: 0.85rem; 81 + } 82 + .skypress-embed__iframe { 83 + width: 100%; 84 + aspect-ratio: 16 / 9; 85 + border: 0; 86 + display: block; 87 + }