feat(infra): migración a tema Apollo, reestructuración del blog y página 'Sobre mí'
All checks were successful
Zola / build-and-deploy (push) Successful in 11s
All checks were successful
Zola / build-and-deploy (push) Successful in 11s
- Implementado tema Apollo como base visual. - Segregación de contenido: Artículos movidos a /blog. - Creación de identidad: Nueva página 'Sobre mí' (about.md). - CI/CD: Actualizado workflow para soporte de submódulos recursivos. - UX: Ajustes en homepage para listado de últimos posts.
This commit is contained in:
parent
0bbe34e8da
commit
4fb49961b4
115 changed files with 6580 additions and 72 deletions
103
public/js/codeblock.js
Normal file
103
public/js/codeblock.js
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
const successIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" class="bi bi-check-lg" viewBox="0 0 16 16">
|
||||
<path d="M13.485 1.85a.5.5 0 0 1 1.065.02.75.75 0 0 1-.02 1.065L5.82 12.78a.75.75 0 0 1-1.106.02L1.476 9.346a.75.75 0 1 1 1.05-1.07l2.74 2.742L12.44 2.92a.75.75 0 0 1 1.045-.07z"/>
|
||||
</svg>`;
|
||||
const errorIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" class="bi bi-x-lg" viewBox="0 0 16 16">
|
||||
<path d="M2.293 2.293a1 1 0 0 1 1.414 0L8 6.586l4.293-4.293a1 1 0 0 1 1.414 1.414L9.414 8l4.293 4.293a1 1 0 0 1-1.414 1.414L8 9.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L6.586 8 2.293 3.707a1 1 0 0 1 0-1.414z"/>
|
||||
</svg>`;
|
||||
const copyIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" class="bi bi-clipboard" viewBox="0 0 16 16">
|
||||
<path d="M10 1.5a.5.5 0 0 1 .5-.5h2a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h2a.5.5 0 0 1 .5.5V3h3V1.5zM6.5 3V2h3v1h-3zm4 0v1h2a1 1 0 0 0-1-1h-2V3zm-5 0H3a1 1 0 0 0-1 1v11a1 1 0 0 0 1 1h9a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1H5.5V3z"/>
|
||||
</svg>`;
|
||||
|
||||
// Function to change icons after copying
|
||||
const changeIcon = (button, isSuccess) => {
|
||||
button.innerHTML = isSuccess ? successIcon : errorIcon;
|
||||
setTimeout(() => {
|
||||
button.innerHTML = copyIcon; // Reset to copy icon
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
// Function to get code text from tables, skipping line numbers
|
||||
const getCodeFromTable = (codeBlock) => {
|
||||
return [...codeBlock.querySelectorAll('tr')]
|
||||
.map(row => row.querySelector('td:last-child')?.innerText ?? '')
|
||||
.join('');
|
||||
};
|
||||
|
||||
// Function to get code text from non-table blocks
|
||||
const getNonTableCode = (codeBlock) => {
|
||||
return codeBlock.textContent.trim();
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Select all `pre` elements containing `code`
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
const pre = entry.target.parentNode;
|
||||
const clipboardBtn = pre.querySelector('.clipboard-button');
|
||||
const label = pre.querySelector('.code-label');
|
||||
|
||||
if (clipboardBtn) {
|
||||
// Adjust the position of the clipboard button when the `code` is not fully visible
|
||||
clipboardBtn.style.right = entry.isIntersecting ? '5px' : `-${entry.boundingClientRect.right - pre.clientWidth + 5}px`;
|
||||
}
|
||||
|
||||
if (label) {
|
||||
// Adjust the position of the label similarly
|
||||
label.style.right = entry.isIntersecting ? '0px' : `-${entry.boundingClientRect.right - pre.clientWidth}px`;
|
||||
}
|
||||
});
|
||||
}, {
|
||||
root: null, // observing relative to viewport
|
||||
rootMargin: '0px',
|
||||
threshold: 1.0 // Adjust this to control when the callback fires
|
||||
});
|
||||
|
||||
document.querySelectorAll('pre code').forEach(codeBlock => {
|
||||
const pre = codeBlock.parentNode;
|
||||
pre.style.position = 'relative'; // Ensure parent `pre` can contain absolute elements
|
||||
|
||||
// Create and append the copy button
|
||||
const copyBtn = document.createElement('button');
|
||||
copyBtn.className = 'clipboard-button';
|
||||
copyBtn.innerHTML = copyIcon;
|
||||
copyBtn.setAttribute('aria-label', 'Copy code to clipboard');
|
||||
pre.appendChild(copyBtn);
|
||||
|
||||
// Attach event listener to copy button
|
||||
copyBtn.addEventListener('click', async () => {
|
||||
// Determine if the code is in a table or not
|
||||
const isTable = codeBlock.querySelector('table');
|
||||
const codeToCopy = isTable ? getCodeFromTable(codeBlock) : getNonTableCode(codeBlock);
|
||||
try {
|
||||
await navigator.clipboard.writeText(codeToCopy);
|
||||
changeIcon(copyBtn, true); // Show success icon
|
||||
} catch (error) {
|
||||
console.error('Failed to copy text: ', error);
|
||||
changeIcon(copyBtn, false); // Show error icon
|
||||
}
|
||||
});
|
||||
|
||||
const langClass = codeBlock.className.match(/language-(\w+)/);
|
||||
const lang = langClass ? langClass[1] : 'default';
|
||||
|
||||
// Create and append the label
|
||||
const label = document.createElement('span');
|
||||
label.className = 'code-label label-' + lang; // Use the specific language class
|
||||
label.textContent = lang.toUpperCase(); // Display the language as label
|
||||
pre.appendChild(label);
|
||||
|
||||
let ticking = false;
|
||||
pre.addEventListener('scroll', () => {
|
||||
if (!ticking) {
|
||||
window.requestAnimationFrame(() => {
|
||||
copyBtn.style.right = `-${pre.scrollLeft}px`;
|
||||
label.style.right = `-${pre.scrollLeft}px`;
|
||||
ticking = false;
|
||||
});
|
||||
ticking = true;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue