fix: add copy-link button and hash deep-linking for blog articles

This commit is contained in:
oliver
2026-05-15 06:49:52 -03:00
parent b8fc96ea0a
commit d4c8760007
+84
View File
@@ -2083,6 +2083,35 @@
color: var(--gold); color: var(--gold);
} }
#article-copy-link {
position: absolute;
top: 14px;
right: 58px;
width: 36px;
height: 36px;
border-radius: 50%;
border: none;
background: rgba(93, 64, 55, 0.08);
color: #8d6e63;
font-size: 1rem;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s, color 0.2s;
z-index: 2;
line-height: 1;
}
#article-copy-link:hover {
background: rgba(230, 81, 0, 0.12);
color: var(--gold);
}
#article-copy-link.copied {
color: var(--gold);
}
/* ── Article actions row (schedule + trial) ──────────────────── */ /* ── Article actions row (schedule + trial) ──────────────────── */
#article-actions { #article-actions {
display: flex; display: flex;
@@ -2744,6 +2773,7 @@
<div id="article-overlay"> <div id="article-overlay">
<div id="article-box"> <div id="article-box">
<button id="article-close" aria-label="Close article">&times;</button> <button id="article-close" aria-label="Close article">&times;</button>
<button id="article-copy-link" aria-label="Copy link to article" title="Copy link to this article">&#128279;</button>
<div class="article-rule"></div> <div class="article-rule"></div>
<div class="article-header"> <div class="article-header">
<div class="article-area" id="article-area"></div> <div class="article-area" id="article-area"></div>
@@ -3674,12 +3704,28 @@
var articleOverlay = document.getElementById('article-overlay'); var articleOverlay = document.getElementById('article-overlay');
var articleClose = document.getElementById('article-close'); var articleClose = document.getElementById('article-close');
var articleCopyLink = document.getElementById('article-copy-link');
var articleTitle = document.getElementById('article-title'); var articleTitle = document.getElementById('article-title');
var articleArea = document.getElementById('article-area'); var articleArea = document.getElementById('article-area');
var articleDate = document.getElementById('article-date'); var articleDate = document.getElementById('article-date');
var articleBody = document.getElementById('article-body'); var articleBody = document.getElementById('article-body');
var articleContent = document.getElementById('article-content'); var articleContent = document.getElementById('article-content');
function slugify(title) {
return (title || '').toLowerCase()
.replace(/[^a-z0-9\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '');
}
function findPostIndexBySlug(slug) {
for (var i = 0; i < allPosts.length; i++) {
if (slugify(allPosts[i].title) === slug) return i;
}
return -1;
}
function openArticle(index) { function openArticle(index) {
var post = allPosts[index]; var post = allPosts[index];
if (!post) return; if (!post) return;
@@ -3690,11 +3736,17 @@
articleBody.scrollTop = 0; articleBody.scrollTop = 0;
articleOverlay.classList.add('is-open'); articleOverlay.classList.add('is-open');
document.body.style.overflow = 'hidden'; document.body.style.overflow = 'hidden';
history.replaceState(null, '', '#post-' + slugify(post.title));
if (articleCopyLink) {
articleCopyLink.innerHTML = '&#128279;';
articleCopyLink.classList.remove('copied');
}
} }
function closeArticle() { function closeArticle() {
articleOverlay.classList.remove('is-open'); articleOverlay.classList.remove('is-open');
document.body.style.overflow = ''; document.body.style.overflow = '';
history.replaceState(null, '', window.location.pathname + window.location.search);
} }
articleClose.addEventListener('click', closeArticle); articleClose.addEventListener('click', closeArticle);
@@ -3705,6 +3757,32 @@
if (e.key === 'Escape' && articleOverlay.classList.contains('is-open')) closeArticle(); if (e.key === 'Escape' && articleOverlay.classList.contains('is-open')) closeArticle();
}); });
if (articleCopyLink) {
articleCopyLink.addEventListener('click', function() {
var url = window.location.href;
function showCopied() {
articleCopyLink.textContent = '\u2713';
articleCopyLink.classList.add('copied');
setTimeout(function() {
articleCopyLink.innerHTML = '&#128279;';
articleCopyLink.classList.remove('copied');
}, 2000);
}
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(url).then(showCopied, showCopied);
} else {
var ta = document.createElement('textarea');
ta.value = url;
ta.style.cssText = 'position:fixed;opacity:0;pointer-events:none';
document.body.appendChild(ta);
ta.select();
try { document.execCommand('copy'); } catch(e) {}
document.body.removeChild(ta);
showCopied();
}
});
}
grid.addEventListener('click', function(e) { grid.addEventListener('click', function(e) {
var btn = e.target.closest('.news-read-btn'); var btn = e.target.closest('.news-read-btn');
if (!btn) return; if (!btn) return;
@@ -3720,6 +3798,12 @@
allPosts = Array.isArray(posts) ? posts : []; allPosts = Array.isArray(posts) ? posts : [];
filteredPosts = allPosts.slice(); filteredPosts = allPosts.slice();
render(); render();
var hash = window.location.hash;
if (hash && hash.indexOf('#post-') === 0) {
var slug = hash.slice(6);
var idx = findPostIndexBySlug(slug);
if (idx !== -1) openArticle(idx);
}
}) })
.catch(function () { .catch(function () {
grid.innerHTML = emptyMarkup( grid.innerHTML = emptyMarkup(