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
+ const blocks = Array.from(dom.querySelectorAll('p')).filter((p) =>
+ /^```[^]+```$/g.test(p.innerText.trim()),
+ );
+ blocks.forEach((block) => {
+ const pre = document.createElement('pre');
+ // Replace code
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;
+}