diff --git a/src/components/status.css b/src/components/status.css index 4630d53b..8cec7e3f 100644 --- a/src/components/status.css +++ b/src/components/status.css @@ -475,7 +475,6 @@ a.card:hover { margin: 8px 0; overflow: auto; width: 100%; - font-size: 90%; border: 1px solid var(--outline-color); background: linear-gradient( to bottom right, @@ -484,6 +483,10 @@ a.card:hover { ); } +.status .content p code { + color: var(--green-color); +} + /* MISC */ .status-aside { diff --git a/src/index.css b/src/index.css index d1f676ef..0f2a23c4 100644 --- a/src/index.css +++ b/src/index.css @@ -36,7 +36,7 @@ :root { --blue-color: CornflowerBlue; --purple-color: mediumpurple; - --green-color: MediumSeaGreen; + --green-color: limegreen; --bg-color: #242526; --bg-faded-color: #18191a; --bg-blur-color: #0009; @@ -187,6 +187,13 @@ select.plain { background-color: transparent; } +pre code, +code { + font-size: 95%; + font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, + monospace; +} + @media (prefers-color-scheme: dark) { img, video { diff --git a/src/utils/enhance-content.js b/src/utils/enhance-content.js index d94d43c2..1034cfc5 100644 --- a/src/utils/enhance-content.js +++ b/src/utils/enhance-content.js @@ -1,97 +1,61 @@ import emojifyText from './emojify-text'; +const fauxDiv = document.createElement('div'); + export default (content, opts = {}) => { const { emojis, postEnhanceDOM = () => {} } = opts; let enhancedContent = content; const dom = document.createElement('div'); dom.innerHTML = enhancedContent; - // 1. Emojis - if (emojis) { - enhancedContent = emojifyText(enhancedContent, emojis); - } - - // 2. Add target="_blank" to all links with no target="_blank" + // Add target="_blank" to all links with no target="_blank" // E.g. `note` in `account` const links = Array.from(dom.querySelectorAll('a:not([target="_blank"])')); links.forEach((link) => { link.setAttribute('target', '_blank'); }); - // 3. Code blocks - // Check for

with markdown-like content "```" - { - const blocks = Array.from(dom.querySelectorAll('p')).filter((p) => - /^```[^]+```$/g.test(p.innerText.trim()), - ); - blocks.forEach((block) => { - const pre = document.createElement('pre'); - const code = document.createElement('code'); - const breaks = block.querySelectorAll('br'); - breaks.forEach((br) => br.replaceWith('\n')); - code.innerHTML = block.innerText - .trim() - // .replace(/^```/g, '') - // .replace(/```$/g, '') - .replace(/^[\n\r]+/, ''); - pre.appendChild(code); - block.replaceWith(pre); - }); - } + // EMOJIS + // ====== + // Convert :shortcode: to + let textNodes = extractTextNodes(dom); + textNodes.forEach((node) => { + let html = node.nodeValue; + if (emojis) { + html = emojifyText(html, emojis); + } + fauxDiv.innerHTML = html; + const nodes = Array.from(fauxDiv.childNodes); + node.replaceWith(...nodes); + }); - // 4. Inline code - { - // Get all text nodes in the DOM - const textNodes = []; - const walk = document.createTreeWalker( - dom, - NodeFilter.SHOW_TEXT, - null, - false, - ); - let node; - while ((node = walk.nextNode())) { - // Only get text that contains markdown-like code syntax - if (/`[^]+`/g.test(node.nodeValue)) { - textNodes.push(node); - } + // INLINE CODE + // =========== + // Convert `code` to code + textNodes = extractTextNodes(dom); + textNodes.forEach((node) => { + let html = node.nodeValue; + if (/`[^`]+`/g.test(html)) { + html = html.replaceAll(/(`[^]+?`)/g, '$1'); } - if (textNodes.length) { - // - Split text nodes into array of text and DOM nodes - // - Replace markdown-like code syntax with element - // - Apply them all back to parent node - textNodes.forEach((node) => { - const parent = node.parentNode; - const text = node.nodeValue; - const nodes = []; - let i = 0; - let j = 0; - let k = 0; - while ((i = text.indexOf('`', j)) !== -1) { - if (i > j) { - nodes.push(document.createTextNode(text.substring(j, i))); - } - j = i + 1; - if ((k = text.indexOf('`', j)) === -1) { - k = j; - } - if (j < k) { - const code = document.createElement('code'); - code.appendChild(document.createTextNode(text.substring(j, k))); - nodes.push(document.createTextNode('`')); - nodes.push(code); - nodes.push(document.createTextNode('`')); - } - j = k + 1; - } - if (j < text.length) { - nodes.push(document.createTextNode(text.substring(j))); - } - nodes.forEach((n) => parent.insertBefore(n, node)); - parent.removeChild(node); - }); - } - } + fauxDiv.innerHTML = html; + const nodes = Array.from(fauxDiv.childNodes); + node.replaceWith(...nodes); + }); + + // CODE BLOCKS + // =========== + // Convert ```code``` to

code
+ const blocks = Array.from(dom.querySelectorAll('p')).filter((p) => + /^```[^]+```$/g.test(p.innerText.trim()), + ); + blocks.forEach((block) => { + const pre = document.createElement('pre'); + // Replace
with newlines + block.querySelectorAll('br').forEach((br) => br.replaceWith('\n')); + pre.innerHTML = `${block.innerText.trim()}`; + block.replaceWith(pre); + }); if (postEnhanceDOM) { postEnhanceDOM(dom); // mutate dom @@ -101,3 +65,18 @@ export default (content, opts = {}) => { return enhancedContent; }; + +function extractTextNodes(dom) { + const textNodes = []; + const walk = document.createTreeWalker( + dom, + NodeFilter.SHOW_TEXT, + null, + false, + ); + let node; + while ((node = walk.nextNode())) { + textNodes.push(node); + } + return textNodes; +}