OverviewFilesCommitsIssuesDocsReleasesPeers

favela-markdown commits / e7d8283

0.1.3: an embedded image is a PREVIEW that opens full size
⛔ Rendered at its natural size a photograph takes over a timeline — a 4000px image is not a post, it is a wall, and everything below it stops being reachable. So the img carries `class="embed"` for the surface to constrain, and is wrapped in `<a class="lightbox">` pointing at the file itself. ⛔ A preview that cannot be opened has thrown the picture away, which is why the anchor is not optional. No script, no second copy of the bytes, no resizing anywhere — the cap is a stylesheet rule and the full file is one click away. `loading="lazy"`, because a timeline should not fetch every picture at once. 63/63, 2390/2390. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
e7d82830f9a2eba04f6f301eb33ea0c77d27910e
by plankatron <fleeky@gmail.com> · 2026-09-07T01:42:01.000Z

Changed files (3) +14 -3

lib/render.js +9 -1
……@@ -87,7 +87,15 @@ function image (node, o) {
8787 const alt = esc(node.alt || '')
8888 const b = blob(o, node.url || '')
8989 if (b) {
90 if (b.mimeType.startsWith('image/')) return '<img src="' + esc(b.src) + '" alt="' + alt + '">'
90 if (b.mimeType.startsWith('image/')) {
91 // ⛔ A PREVIEW, wrapped in a link to the file itself. An embed rendered at its natural size
92 // takes over a timeline — a 4000px photo is not a post, it is a wall — but a preview that
93 // cannot be opened has thrown the picture away. The surface constrains `.embed` and the
94 // anchor gives the full file back, with no script involved.
95 return '<a class="lightbox" href="' + esc(b.src) + '">' +
96 '<img class="embed" loading="lazy" src="' + esc(b.src) + '" alt="' + alt + '">' +
97 '</a>'
98 }
9199 // ⛔ `![]()` on a video is deliberate, not a mistake: §3.5 has the embed pipeline consuming
92100 // images in source order, and a video is the same authoring gesture with different bytes.
93101 if (b.mimeType.startsWith('video/')) {
package.json +1 -1
……@@ -1,6 +1,6 @@
11{
22 "name": "favela-markdown",
3 "version": "0.1.2",
3 "version": "0.1.3",
44 "description": "The P2P-native markdown record layer for favela. A post is a markdown document; atproto and ActivityPub are projections derived from it on read.",
55 "type": "module",
66 "license": "MIT",
test/render.test.js +4 -1
……@@ -102,7 +102,10 @@ const resolver = (mimeType, src = '/blob/' + OID) => ({ resolveBlob: () => ({ sr
102102
103103test('★ an image blob renders an <img> from the surface\'s own src', (t) => {
104104 const html = render('![a cat](' + BLOB + ')', resolver('image/png'))
105 t.ok(/<img src="\/blob\/a3f9[a-f0-9]*" alt="a cat">/.test(html), 'src and alt both survive')
105 t.ok(/<img class="embed" loading="lazy" src="\/blob\/a3f9[a-f0-9]*" alt="a cat">/.test(html),
106 'src and alt both survive')
107 // ⛔ Wrapped in a link to the file, so a constrained preview does not throw the picture away.
108 t.ok(/<a class="lightbox" href="\/blob\//.test(html), 'and it opens full size, with no script')
106109})
107110
108111test('★ a video blob renders <video controls>, not a broken image', (t) => {