Something went wrong. Try again.
a new old personal site matmanna.dev
tailwind jekyll
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366<div id="toc-container" class="relative"> <div id="toc-placeholder" class="blur-sm my-4 rounded-md w-full bg-primary-200 dark:bg-primary-800"></div>
<div id="table-of-contents" class="text-xs bg-primary-200 dark:bg-primary-800 rounded-md p-2 px-0 w-full transition-all duration-100 ease-in-out"> <h2 class="hidden font-bold mt-0 ml-3"> {{ page.title }} </h2>
<ul class="my-0 text-xs space-y-4">
<li> <div class="inline flex-row items-center gap-3">{% include icon.html name="fas fa-forward-fast" class="w-3 h-3 inline" %} <strong>TLDR</strong> {{ page.description}} </div> </li>
<li> <p class="my-0 w-fit"> {% include icon.html name="fas fa-calendar" class="w-3 h-3 inline" %} <strong>Published</strong> {{ page.date | date_to_string }} </p> </li>
<li> <div class="inline flex-row items-center gap-3">{% include icon.html name="fas fa-clock" class="w-3 h-3 inline" %} <strong>Time</strong> {% capture words %} {{ content | number_of_words | minus: 180 }} {% endcapture %} {% unless words contains '-' %} {{ words | plus: 150 | divided_by: 150 | append: ' minutes to read' }} {% endunless %} </div> </li>
{% if page.authors.size > 0 %} <li> <div class="{% if page.authors.size > 1 %}flex flex-col{% else %}flex flex-row items-center gap-2{% endif %}"> <div class="flex flex-row gap-2"> {% include icon.html name="fas fa-feather-pointed" class="w-3 h-3" %} <strong>AUTHORED BY</strong> </div> {% if page.authors.size == 1 %} <a href="https://github.com/{{ author }}" class="flex flex-row items-center gap-2"> <img src="https://github.com/{{ page.authors[0] }}.png?size=20" class="my-0 w-6 h-6 rounded-full" /> @{{ page.authors[0] }} </a> {% else %}
<ul> {% for author in page.authors %} <li> <a href="https://github.com/{{ author }}" class="flex flex-row items-center gap-2"> <img src="https://github.com/{{ author }}.png?size=20" class="my-0 w-6 h-6 rounded-full" /> @{{ author }} </a> </li> {% endfor %} </ul> {% endif %} </div> </li> {% endif %}
{% if page.tags.size > 0 %} <li> <div class="flex flex-row gap-2 items-center"> {% include icon.html name="fas fa-hashtag" class="w-3 h-3" %} <p class="my-0 mr-1"><strong>Tags </strong></p> {% for tag in page.tags %} <a href="/tag/{{ tag | downcase | replace: ' ', '-' }}/" class="no-underline"> <div class="bg-primary-300 dark:bg-primary-900 py-1 text-primary-800 dark:text-primary-200 items-center px-2 rounded-md flex flex-row gap-2 h-fit text-xs"> <!-- <i class="{{ tag }}"></i> -->{{ tag }} </div> </a> {% endfor %} </div> </li> {% endif %}
{% if page.mentions %} <li> <div class="flex flex-row items-center gap-3">{% include icon.html name="fas fa-newspaper" class="w-3 h-3" %}<strong>MENTIONED BY</strong></div> <ul> {% for mention in page.mentions %} <li> <a href="{{ mention.url }}" class="text-primary-600 dark:text-primary-400 hover:underline flex flex-row gap-2 items-center"> {% include icon.html name=mention.icon class="w-3 h-3 no-underline" %}{{ mention.title }} </a> </li> {% endfor %} </ul> </li> {% endif %}
</ul> {%if page.toc %} <h4 class="text-xs font-bold mt-4 mb-2 ml-3"> Table of Contents</h4> {% endif %}
{% include toc.html html=content anchor_class="toc-link" %} </div>
<div id="toc-navbar" class="hidden! bg-primary-300 dark:bg-primary-950 rounded-t-md p-2 w-full flex justify-between items-center opacity-0 transition-all duration-100 ease-in-out transform -translate-y-4"> <div class="flex items-center gap-2"> <span class="text-sm">{{ site.data.foot.emoji }}</span> <span class="font-bold text-sm"> {{ page.title }}</span> </div> <div class="relative group"> <button class="flex items-center text-sm gap-1 px-2 py-1 rounded hover:bg-primary-400 dark:hover:bg-primary-700 transition-colors"> <a href="#top">Top</a>{% include icon.html name="fa-solid fa-chevron-down" class="w-3 h-3 text-xs" %} </button> <div class="absolute right-0 mt-0 w-40 bg-white dark:bg-primary-900 rounded-md shadow-lg overflow-hidden z-10 hidden group-hover:block"> <div class="py-1"> {% for page in site.data.nav.pages %} <a href="{{ page.url | relative_url }}" class="block px-4 py-2 text-sm hover:bg-primary-200 dark:hover:bg-primary-700"> /{{ page.title }} </a> {% endfor %} </div> </div> </div> </div></div>
<script> document.addEventListener('DOMContentLoaded', function () { const toc = document.getElementById('table-of-contents'); const tocNavbar = document.getElementById('toc-navbar'); const placeholder = document.getElementById('toc-placeholder'); const tocContainer = document.getElementById('toc-container'); let originalRect = null; let isFixed = false; let targetPosition = { x: 0, y: 0, scale: 1 };
function updateOriginalPosition() { toc.style.position = 'static'; // toc.querySelector('h4').style.display = 'none'; // toc.querySelectorAll('h4')[1].style.display = 'block';
toc.style.transform = 'none'; placeholder.style.display = 'none'; tocNavbar.style.display = 'none';
void toc.offsetWidth;
originalRect = toc.getBoundingClientRect(); placeholder.style.height = originalRect.height + 'px';
const fixedTop = 80;
targetPosition = { x: 0, y: fixedTop, width: originalRect.width }; }
function toggleFixedPosition() { if (!originalRect) { updateOriginalPosition(); }
if (window.innerWidth >= 1280) { const shouldBeFixed = window.scrollY > (tocContainer.offsetTop - 5);
if (shouldBeFixed !== isFixed) { isFixed = shouldBeFixed;
if (isFixed) { const viewportWidth = window.innerWidth; let fixedLeft, maxWidth;
if (viewportWidth > 1600) { fixedLeft = viewportWidth * 0.03; maxWidth = Math.min(450, viewportWidth * 0.25); } else if (viewportWidth > 1280) { fixedLeft = viewportWidth * 0.025; maxWidth = Math.min(400, viewportWidth * 0.22); } else { fixedLeft = viewportWidth * 0.03; maxWidth = Math.min(350, viewportWidth * 0.2); } toc.querySelector('h4').innerHTML = '{{ page.title }}'; // toc.querySelector('h4').style.display = 'block';
targetPosition.x = fixedLeft; targetPosition.width = maxWidth;
toc.style.position = 'fixed'; toc.style.top = targetPosition.y + 'px'; toc.style.left = targetPosition.x + 'px'; toc.style.width = targetPosition.width + 'px'; toc.style.borderTopLeftRadius = '0'; toc.style.borderTopRightRadius = '0'; placeholder.style.display = 'block';
tocNavbar.style.display = 'flex'; tocNavbar.style.position = 'fixed'; tocNavbar.style.top = (targetPosition.y - tocNavbar.offsetHeight) + 'px'; tocNavbar.style.left = targetPosition.x + 'px'; tocNavbar.style.width = targetPosition.width + 'px'; tocNavbar.style.opacity = '0'; tocNavbar.style.transform = 'translateY(-16px)';
void tocNavbar.offsetWidth;
requestAnimationFrame(() => { tocNavbar.style.opacity = '1'; tocNavbar.style.transform = 'translateY(0)'; }); } else { tocNavbar.style.opacity = '0'; tocNavbar.style.transform = 'translateY(-16px)';
setTimeout(() => { toc.style.position = 'absolute'; toc.style.top = placeholder.offsetTop + 'px'; toc.style.left = ''; toc.style.width = ''; toc.style.maxWidth = ''; // toc.querySelector('h4').style.display = 'none'; // toc.querySelectorAll('h4')[1].style.display = 'block'; toc.style.borderTopLeftRadius = ''; toc.style.borderTopRightRadius = '';
tocNavbar.style.display = 'none';
void toc.offsetWidth;
setTimeout(() => { placeholder.style.display = 'none'; toc.style.position = 'static'; }, 50); }, 100); } } } else if (isFixed) { isFixed = false; toc.style.position = 'static'; toc.style.width = 'auto'; toc.style.transform = 'none'; placeholder.style.display = 'none'; tocNavbar.style.display = 'none'; } }
let ticking = false; window.addEventListener('scroll', function () { if (!ticking) { window.requestAnimationFrame(function () { toggleFixedPosition(); ticking = false; }); ticking = true; } });
let scrollTimeout; window.addEventListener('scroll', () => { clearTimeout(scrollTimeout); scrollTimeout = setTimeout(() => { toggleFixedPosition(); }, 50); }, { passive: true });
window.addEventListener('resize', function () { updateOriginalPosition(); toggleFixedPosition(); });
updateOriginalPosition(); toggleFixedPosition(); });</script>
<script> document.addEventListener('DOMContentLoaded', function () { const toc = document.getElementById('table-of-contents'); const tocLinks = Array.from(document.querySelectorAll('#table-of-contents a.toc-link')); if (!tocLinks.length) return;
const items = tocLinks.map(a => { const href = a.getAttribute('href') || ''; const id = href.startsWith('#') ? href.slice(1) : null; const target = id ? document.getElementById(id) : null; const ring = a.querySelector('.toc-ring'); const li = a.closest('li'); const radius = ring ? Number(ring.getAttribute('r')) : 8; const circumference = 2 * Math.PI * radius; if (ring) { ring.style.strokeDasharray = `${circumference}`; ring.style.strokeDashoffset = `${circumference}`; } return { a, id, target, ring, circumference, li }; }).filter(i => i.target && i.ring && i.li);
function clamp(v, lo = 0, hi = 1) { return Math.max(lo, Math.min(hi, v)); }
function updateProgress() { const tocComputedStyle = window.getComputedStyle(toc); const tocIsFixed = tocComputedStyle && tocComputedStyle.position !== 'fixed'; if (tocIsFixed) { for (const item of items) { if (item.ring) item.ring.style.strokeDashoffset = String(item.circumference); if (item.a && item.a.querySelector('.toc-bullet')) item.a.querySelector('.toc-bullet').style.display = 'none'; } return; } else { for (const item of items) { if (item.a && item.a.querySelector('.toc-bullet')) item.a.querySelector('.toc-bullet').style.display = ''; } }
const scrollY = window.scrollY || window.pageYOffset; const viewportH = window.innerHeight;
const basePct = new Map(); for (const item of items) { const el = item.target; const rect = el.getBoundingClientRect(); const elTop = rect.top + scrollY; const elHeight = Math.max(rect.height, 1); const viewed = (scrollY + viewportH - elTop) / (elHeight + viewportH); basePct.set(item.a, clamp(viewed, 0, 1)); }
for (const item of items) { let pct = basePct.get(item.a) || 0;
const descendantAnchors = Array.from(item.li.querySelectorAll('a.toc-link')).filter(a => a !== item.a); const descendantItems = descendantAnchors.map(a => items.find(it => it.a === a)).filter(Boolean); if (descendantItems.length > 0) { const vals = descendantItems.map(di => basePct.get(di.a) || 0); const sum = vals.reduce((s, v) => s + v, 0); pct = sum / vals.length; }
const offset = item.circumference * (1 - pct); if (item.ring) item.ring.style.strokeDashoffset = String(offset); } }
let ticking = false; window.addEventListener('scroll', () => { if (!ticking) { requestAnimationFrame(() => { updateProgress(); ticking = false; }); ticking = true; } }, { passive: true });
window.addEventListener('resize', updateProgress); updateProgress(); });</script>