a demonstration replicated social networking web app built with anproto
wiredove.net/
social
ed25519
protocols
1import { h } from 'h'
2import { apds } from 'apds'
3import { marked } from 'https://esm.sh/gh/evbogue/bog5@de70376265/lib/marked.esm.js'
4import { send } from './send.js'
5
6const renderer = new marked.Renderer()
7
8const linkHashtags = (words) => {
9 for (let i = 0; i < words.length; i++) {
10 let word = words[i]
11 if (!word.startsWith('#')) { continue }
12 let end
13 if (['.', ',', '?', ':', '!'].some(char => word.endsWith(char))) {
14 end = word[word.length - 1]
15 word = word.substring(0, word.length - 1)
16 }
17 let hashtag = "<a href='#?" + word + "'>" + word + "</a>"
18 if (end) { hashtag += end }
19 words[i] = hashtag
20 }
21 return words
22}
23
24renderer.paragraph = function (paragraph) {
25 const images = paragraph.match(/<img\b[^>]*>/gi) || []
26 if (images.length) {
27 const textOnly = paragraph
28 .replace(/<img\b[^>]*>/gi, ' ')
29 .replace(/<br\s*\/?>/gi, ' ')
30 .replace(/\s+/g, ' ')
31 .trim()
32
33 if (!textOnly) {
34 return `<div class="post-image-row">${images.join('')}</div>`
35 }
36
37 const linked = linkHashtags(textOnly.split(' ')).join(' ')
38 return '<p>' + linked + '</p><div class="post-image-row">' + images.join('') + '</div>'
39 }
40
41 return '<p>' + linkHashtags(paragraph.split(' ')).join(' ') + '</p>'
42}
43
44renderer.link = function (href, title, text) {
45 if (href.length == 44 && !href.startsWith('http')) {
46 href = '#' + href
47 return marked.Renderer.prototype.link.call(this, href, title, text);
48 } else {
49 const m = marked.Renderer.prototype.link.call(this, href, title, text)
50 return m
51 }
52}
53
54renderer.image = function (src, title, text) {
55 if (src && src.length === 44) {
56 apds.get(src).then(async (img) => {
57 if (img) {
58 const image = document.getElementById('inlineimage' + src)
59 if (image) {
60 image.src = img
61 }
62 } else {
63 await send(src)
64 }
65 })
66 const altText = text ? text.replace(/"/g, '"') : 'Post image'
67 return `<img class="post-image" data-hash="${src}" id="inlineimage${src}" alt="${altText}" loading="lazy" tabindex="0" />`
68 }
69}
70
71marked.setOptions({
72 renderer: renderer
73})
74
75export const markdown = async (txt) => {
76 const rendered = marked(txt || '')
77 // If markdown splits image-only paragraphs (e.g. due blank lines), merge adjacent rows.
78 return rendered.replace(/<\/div>\s*<div class="post-image-row">/g, '')
79}