slack status without the slack status.zzstoatzz.io/
quickslice

Merge pull request #43 from zzstoatzz/feature/markdown-links-and-subtle-glow

Reduce emoji pulse + Markdown links in status text

authored by

nate nowack and committed by
GitHub
904bf445 db1d8d6f

+94 -5
+57
static/markdown.js
··· 1 + // Lightweight markdown link renderer for status text 2 + // Converts [text](url) into <a href> with basic sanitization 3 + (function () { 4 + const MD_LINK_RE = /\[([^\]]+)\]\(([^)]+)\)/g; 5 + 6 + function escapeHtml(str) { 7 + return String(str) 8 + .replace(/&/g, "&amp;") 9 + .replace(/</g, "&lt;") 10 + .replace(/>/g, "&gt;") 11 + .replace(/"/g, "&quot;") 12 + .replace(/'/g, "&#39;"); 13 + } 14 + 15 + function normalizeUrl(url) { 16 + let u = url.trim(); 17 + // If no scheme and looks like a domain, prefix with https:// 18 + if (!/^([a-z]+:)?\/\//i.test(u)) { 19 + u = 'https://' + u; 20 + } 21 + try { 22 + const parsed = new URL(u); 23 + if (parsed.protocol === 'http:' || parsed.protocol === 'https:') { 24 + return parsed.toString(); 25 + } 26 + return null; // disallow other protocols 27 + } catch (_) { 28 + return null; 29 + } 30 + } 31 + 32 + function linkifyMarkdown(text) { 33 + return text.replace(MD_LINK_RE, (_m, label, url) => { 34 + const safeUrl = normalizeUrl(url); 35 + const safeLabel = escapeHtml(label); 36 + if (!safeUrl) return `[${safeLabel}](${escapeHtml(url)})`; 37 + return `<a href="${safeUrl}" target="_blank" rel="noopener noreferrer nofollow">${safeLabel}</a>`; 38 + }); 39 + } 40 + 41 + function renderMarkdownLinksIn(root) { 42 + const scope = root || document; 43 + const nodes = scope.querySelectorAll('.status-text:not([data-md-rendered]), .history-text:not([data-md-rendered])'); 44 + nodes.forEach((el) => { 45 + const original = el.textContent || ''; 46 + const rendered = linkifyMarkdown(original); 47 + if (rendered !== original) { 48 + el.innerHTML = rendered; 49 + } 50 + el.setAttribute('data-md-rendered', 'true'); 51 + }); 52 + } 53 + 54 + // Expose globally 55 + window.renderMarkdownLinksIn = renderMarkdownLinksIn; 56 + })(); 57 +
+11 -1
templates/base.html
··· 28 28 29 29 <!-- Shared Settings Module --> 30 30 <script src="/static/settings.js"></script> 31 + <!-- Markdown link renderer for statuses --> 32 + <script src="/static/markdown.js"></script> 31 33 32 34 <!-- Apply User Settings --> 33 35 <script> ··· 187 189 } 188 190 }); 189 191 </script> 192 + <script> 193 + // Render markdown links in any status/history text on load 194 + document.addEventListener('DOMContentLoaded', function () { 195 + if (window.renderMarkdownLinksIn) { 196 + window.renderMarkdownLinksIn(document); 197 + } 198 + }); 199 + </script> 190 200 </body> 191 - </html> 201 + </html>
+11 -1
templates/feed.html
··· 655 655 color: var(--text-primary); 656 656 } 657 657 658 + .status-text a { 659 + color: var(--accent); 660 + text-decoration: underline; 661 + text-underline-offset: 2px; 662 + } 663 + 658 664 .status-meta { 659 665 font-size: 0.875rem; 660 666 color: var(--text-tertiary); ··· 950 956 `; 951 957 952 958 statusList.appendChild(statusItem); 959 + // Render markdown links in the newly added item 960 + if (window.renderMarkdownLinksIn) { 961 + window.renderMarkdownLinksIn(statusItem); 962 + } 953 963 }); 954 964 955 965 // Re-initialize timestamps for newly added elements ··· 1197 1207 }); 1198 1208 }); 1199 1209 </script> 1200 - {%endblock content%} 1210 + {%endblock content%}
+15 -3
templates/status.html
··· 636 636 filter: drop-shadow(0 0 0 transparent); 637 637 } 638 638 50% { 639 - transform: scale(1.018); 640 - filter: drop-shadow(0 0 10px var(--accent)); 639 + transform: scale(1.01); 640 + filter: drop-shadow(0 0 6px var(--accent)); 641 641 } 642 642 } 643 643 ··· 674 674 font-size: 1.25rem; 675 675 color: var(--text-primary); 676 676 margin: 0; 677 + } 678 + 679 + .status-text a { 680 + color: var(--accent); 681 + text-decoration: underline; 682 + text-underline-offset: 2px; 677 683 } 678 684 679 685 .no-status .status-text { ··· 1070 1076 overflow: hidden; 1071 1077 text-overflow: ellipsis; 1072 1078 white-space: nowrap; 1079 + } 1080 + 1081 + .history-text a { 1082 + color: var(--accent); 1083 + text-decoration: underline; 1084 + text-underline-offset: 2px; 1073 1085 } 1074 1086 1075 1087 .history-time { ··· 1952 1964 }); 1953 1965 }); 1954 1966 </script> 1955 - {%endblock content%} 1967 + {%endblock content%}