switched theme

This commit is contained in:
2026-04-02 13:14:18 +03:00
parent 0c26d89eae
commit e31dbbb16c
192 changed files with 215173 additions and 1463 deletions
BIN
View File
Binary file not shown.
-54
View File
@@ -1,54 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>50x Borked</title>
<meta name="author" content="Reilly Tucker Siemens">
<meta name="description" content="50x Borked">
<meta property="og:site_name" content="tuckersiemens.com">
<meta property="og:locale" content="en-us">
<meta property="og:title" content="50x Borked">
<meta property="og:description" content="50x Borked">
<meta property="og:image" content="https://tuckersiemens.com/self.jpg">
<meta property="twitter:card" content="summary">
<meta property="twitter:site" content="@TuckerSiemens">
<meta property="twitter:title" content="50x Borked">
<meta property="twitter:description" content="50x Borked">
<meta property="twitter:image" content="https://tuckersiemens.com/self.jpg">
<link rel="dns-prefetch" href="https://fonts.googleapis.com/">
<link rel="stylesheet" href="/site.css">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
</head>
<body>
<main id="content">
<header>
<h1>50[0234] Borked</h1>
</header>
<figure>
<video src="50x.mp4" type="video/mp4" autoplay loop controls></video>
<figcaption>
Video from <a href="https://www.sakugabooru.com/post/show/174648">sakugabooru</a>
</figcaption>
</figure>
</main>
<footer>
<nav>
<a href="/">Home</a>
⬡
<a href="/posts">Posts</a>
⬡
<a href="/tags">Tags</a>
⬡
<a href="/contact">Contact</a>
</nav>
<p>
©
2021 Reilly Tucker Siemens ⬡
<a href="https://github.com/reillysiemens/tuckersiemens.com">Source</a>
⬡
<a href="/rss.xml">Feed</a>
</p>
</footer>
</body>
</html>
BIN
View File
Binary file not shown.
File diff suppressed because it is too large Load Diff
+66
View File
@@ -0,0 +1,66 @@
/* Outfit */
@font-face {
font-family: 'Outfit';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url('../fonts/outfit/outfit-v15-latin-300.woff2') format('woff2');
}
@font-face {
font-family: 'Outfit';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('../fonts/outfit/outfit-v15-latin-regular.woff2') format('woff2');
}
@font-face {
font-family: 'Outfit';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('../fonts/outfit/outfit-v15-latin-500.woff2') format('woff2');
}
@font-face {
font-family: 'Outfit';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url('../fonts/outfit/outfit-v15-latin-600.woff2') format('woff2');
}
@font-face {
font-family: 'Outfit';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('../fonts/outfit/outfit-v15-latin-700.woff2') format('woff2');
}
/* JetBrains Mono (official release — full ligature support) */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('../fonts/jetbrains-mono/JetBrainsMono-Regular.woff2') format('woff2');
}
@font-face {
font-family: 'JetBrains Mono';
font-style: italic;
font-weight: 400;
font-display: swap;
src: url('../fonts/jetbrains-mono/JetBrainsMono-Italic.woff2') format('woff2');
}
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('../fonts/jetbrains-mono/JetBrainsMono-Bold.woff2') format('woff2');
}
@font-face {
font-family: 'JetBrains Mono';
font-style: italic;
font-weight: 700;
font-display: swap;
src: url('../fonts/jetbrains-mono/JetBrainsMono-BoldItalic.woff2') format('woff2');
}
+1128
View File
File diff suppressed because it is too large Load Diff
-3
View File
@@ -1,3 +0,0 @@
<svg height="128" width="128" xmlns="http://www.w3.org/2000/svg">
<polygon points="64 4, 124 32, 124 96, 64 124, 4 96, 4 32" fill="#b16286"/>
</svg>

Before

Width:  |  Height:  |  Size: 151 B

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
google-site-verification: google52b8421f5e6826b8.html
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 KiB

+108
View File
@@ -0,0 +1,108 @@
(function () {
'use strict';
var body = document.getElementById('term-body');
if (!body) return;
var path = window.location.pathname;
var promptUser = body.dataset.prompt || 'user@pwnbox';
var hostname = body.dataset.hostname || 'pwnbox';
var whoami = body.dataset.whoami || 'l0st_r3ader';
var links = [
{ href: body.dataset.linkHome, label: '~/home' },
{ href: body.dataset.linkPosts, label: '~/posts' },
{ href: body.dataset.linkTags, label: '~/tags' },
];
var steps = [
{ type: 'cmd', text: 'cd ' + path },
{ type: 'out-error', text: 'bash: cd: ' + path + ': No such file or directory' },
{ type: 'cmd', text: 'whoami' },
{ type: 'out', text: whoami },
{ type: 'cmd', text: 'ls ~/recovery/' },
{ type: 'ls' },
];
var CHAR_DELAY = 28;
var LINE_DELAY = 180;
function ps1() {
var s = document.createElement('span');
s.className = 'term-ps1';
s.textContent = promptUser + '@' + hostname + ':~$ ';
return s;
}
function appendCmdLine(text, callback) {
var row = document.createElement('div');
row.className = 'term-row';
row.appendChild(ps1());
var input = document.createElement('span');
input.className = 'term-input';
row.appendChild(input);
body.appendChild(row);
var i = 0;
function tick() {
if (i < text.length) {
input.textContent += text[i++];
setTimeout(tick, CHAR_DELAY);
} else {
setTimeout(callback, LINE_DELAY);
}
}
tick();
}
function appendOut(text, extraClass, callback) {
var div = document.createElement('div');
div.className = 'term-out' + (extraClass ? ' ' + extraClass : '');
div.textContent = text;
body.appendChild(div);
setTimeout(callback, LINE_DELAY);
}
function appendLs(callback) {
var div = document.createElement('div');
div.className = 'term-out-ls';
links.forEach(function (l) {
var a = document.createElement('a');
a.href = l.href;
a.textContent = l.label;
div.appendChild(a);
});
body.appendChild(div);
setTimeout(callback, LINE_DELAY);
}
function appendCursor() {
var row = document.createElement('div');
row.className = 'term-row';
row.appendChild(ps1());
var cur = document.createElement('span');
cur.className = 'term-cursor';
cur.setAttribute('aria-hidden', 'true');
row.appendChild(cur);
body.appendChild(row);
}
function runSteps(i) {
if (i >= steps.length) {
appendCursor();
return;
}
var step = steps[i];
var next = function () {
runSteps(i + 1);
};
if (step.type === 'cmd') appendCmdLine(step.text, next);
else if (step.type === 'out-error') appendOut(step.text, 'term-out-error', next);
else if (step.type === 'out') appendOut(step.text, 'term-out-muted', next);
else if (step.type === 'ls') appendLs(next);
else next();
}
runSteps(0);
})();
-1
View File
@@ -1 +0,0 @@
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={757:function(t){t.exports=e}},n={};function r(e){var o=n[e];if(void 0!==o)return o.exports;var i=n[e]={exports:{}};return t[e](i,i.exports,r),i.exports}r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var o={};r.d(o,{default:function(){return p}});var i=r(757),a=r.n(i);const l=function(e,t,n){let r=n,o=0;const i=e.length;for(;r<t.length;){const n=t[r];if(o<=0&&t.slice(r,r+i)===e)return r;"\\"===n?r++:"{"===n?o++:"}"===n&&o--,r++}return-1},s=/^\\begin{/;var d=function(e,t){let n;const r=[],o=new RegExp("("+t.map((e=>e.left.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"))).join("|")+")");for(;n=e.search(o),-1!==n;){n>0&&(r.push({type:"text",data:e.slice(0,n)}),e=e.slice(n));const o=t.findIndex((t=>e.startsWith(t.left)));if(n=l(t[o].right,e,t[o].left.length),-1===n)break;const i=e.slice(0,n+t[o].right.length),a=s.test(i)?i:e.slice(t[o].left.length,n);r.push({type:"math",data:a,rawData:i,display:t[o].display}),e=e.slice(n+t[o].right.length)}return""!==e&&r.push({type:"text",data:e}),r};const c=function(e,t){const n=d(e,t.delimiters);if(1===n.length&&"text"===n[0].type)return null;const r=document.createDocumentFragment();for(let e=0;e<n.length;e++)if("text"===n[e].type)r.appendChild(document.createTextNode(n[e].data));else{const o=document.createElement("span");let i=n[e].data;t.displayMode=n[e].display;try{t.preProcess&&(i=t.preProcess(i)),a().render(i,o,t)}catch(o){if(!(o instanceof a().ParseError))throw o;t.errorCallback("KaTeX auto-render: Failed to parse `"+n[e].data+"` with ",o),r.appendChild(document.createTextNode(n[e].rawData));continue}r.appendChild(o)}return r},f=function(e,t){for(let n=0;n<e.childNodes.length;n++){const r=e.childNodes[n];if(3===r.nodeType){let o=r.textContent,i=r.nextSibling,a=0;for(;i&&i.nodeType===Node.TEXT_NODE;)o+=i.textContent,i=i.nextSibling,a++;const l=c(o,t);if(l){for(let e=0;e<a;e++)r.nextSibling.remove();n+=l.childNodes.length-1,e.replaceChild(l,r)}else n+=a}else if(1===r.nodeType){const e=" "+r.className+" ";-1===t.ignoredTags.indexOf(r.nodeName.toLowerCase())&&t.ignoredClasses.every((t=>-1===e.indexOf(" "+t+" ")))&&f(r,t)}}};var p=function(e,t){if(!e)throw new Error("No element provided to render");const n={};for(const e in t)t.hasOwnProperty(e)&&(n[e]=t[e]);n.delimiters=n.delimiters||[{left:"$$",right:"$$",display:!0},{left:"\\(",right:"\\)",display:!1},{left:"\\begin{equation}",right:"\\end{equation}",display:!0},{left:"\\begin{align}",right:"\\end{align}",display:!0},{left:"\\begin{alignat}",right:"\\end{alignat}",display:!0},{left:"\\begin{gather}",right:"\\end{gather}",display:!0},{left:"\\begin{CD}",right:"\\end{CD}",display:!0},{left:"\\[",right:"\\]",display:!0}],n.ignoredTags=n.ignoredTags||["script","noscript","style","textarea","pre","code","option"],n.ignoredClasses=n.ignoredClasses||[],n.errorCallback=n.errorCallback||console.error,n.macros=n.macros||{},f(e,n)};return o=o.default}()}));
File diff suppressed because it is too large Load Diff
+68
View File
@@ -0,0 +1,68 @@
document.addEventListener('DOMContentLoaded', () => {
// Select all <pre> elements that contain code blocks
const preBlocks = document.querySelectorAll('pre');
const copyIcon =
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg>';
const checkIcon =
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>';
preBlocks.forEach((pre) => {
// Create the copy button
const button = document.createElement('button');
button.innerHTML = copyIcon;
button.className = 'copy-code-btn';
button.setAttribute('aria-label', 'Copy code');
button.setAttribute('title', 'Copy code');
// Add the button to the <pre> block
// Assuming pre is position relative in CSS
pre.appendChild(button);
button.addEventListener('click', async () => {
// Find the <code> element within the <pre>
const code = pre.querySelector('code');
if (!code) return;
// Get the text content, removing any leading/trailing whitespace
const text = code.innerText;
try {
if (navigator.clipboard && window.isSecureContext) {
// Modern Async Clipboard API
await navigator.clipboard.writeText(text);
} else {
// Fallback for insecure contexts (like local network IPs via HTTP)
const textArea = document.createElement('textarea');
textArea.value = text;
// Move outside the screen to make it invisible
textArea.style.position = 'absolute';
textArea.style.left = '-999999px';
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
textArea.remove();
}
// Add visual feedback
button.innerHTML = checkIcon;
button.classList.add('copied');
button.setAttribute('aria-label', 'Copied');
button.setAttribute('title', 'Copied!');
// Revert back after 2 seconds
setTimeout(() => {
button.innerHTML = copyIcon;
button.classList.remove('copied');
button.setAttribute('aria-label', 'Copy code');
button.setAttribute('title', 'Copy code');
}, 2000);
} catch (err) {
console.error('Failed to copy code: ', err);
}
});
});
});
+10
View File
File diff suppressed because one or more lines are too long
+32
View File
@@ -0,0 +1,32 @@
(function () {
'use strict';
const refs = document.querySelectorAll('sup.footnote-reference');
if (!refs.length) return;
refs.forEach(function (sup) {
const a = sup.querySelector('a');
if (!a) return;
const targetId = a.getAttribute('href').replace(/^#/, '');
const refId = 'fnref-' + targetId;
sup.id = refId;
const def = document.getElementById(targetId);
if (!def) return;
// Append ↩ to the last <p> inside the definition
const paras = def.querySelectorAll('p');
const lastPara = paras[paras.length - 1];
if (!lastPara) return;
const backlink = document.createElement('a');
backlink.href = '#' + refId;
backlink.className = 'footnote-backref';
backlink.setAttribute('aria-label', 'Back to reference');
backlink.textContent = '↩';
lastPara.appendChild(document.createTextNode('\u00a0'));
lastPara.appendChild(backlink);
});
})();
+13
View File
@@ -0,0 +1,13 @@
(function () {
'use strict';
document.querySelectorAll('.post-body h2[id], .post-body h3[id]').forEach(function (heading) {
var a = document.createElement('a');
a.href = '#' + heading.id;
a.className = 'heading-anchor';
a.setAttribute('aria-label', 'Link to this section');
a.setAttribute('tabindex', '-1');
a.textContent = '#';
heading.appendChild(a);
});
})();
-1
View File
File diff suppressed because one or more lines are too long
+170
View File
@@ -0,0 +1,170 @@
!(function (e, t) {
'object' == typeof exports && 'object' == typeof module
? (module.exports = t(require('katex')))
: 'function' == typeof define && define.amd
? define(['katex'], t)
: 'object' == typeof exports
? (exports.renderMathInElement = t(require('katex')))
: (e.renderMathInElement = t(e.katex));
})('undefined' != typeof self ? self : this, function (e) {
return (function () {
'use strict';
var t = {
771: function (t) {
t.exports = e;
},
},
r = {};
function n(e) {
var i = r[e];
if (void 0 !== i) return i.exports;
var a = (r[e] = { exports: {} });
return (t[e](a, a.exports, n), a.exports);
}
((n.n = function (e) {
var t =
e && e.__esModule
? function () {
return e.default;
}
: function () {
return e;
};
return (n.d(t, { a: t }), t);
}),
(n.d = function (e, t) {
for (var r in t)
n.o(t, r) && !n.o(e, r) && Object.defineProperty(e, r, { enumerable: !0, get: t[r] });
}),
(n.o = function (e, t) {
return Object.prototype.hasOwnProperty.call(e, t);
}));
var i = {};
return (
(function () {
n.d(i, {
default: function () {
return s;
},
});
var e = n(771),
t = n.n(e),
r = function (e, t, r) {
for (var n = r, i = 0, a = e.length; n < t.length; ) {
var o = t[n];
if (i <= 0 && t.slice(n, n + a) === e) return n;
('\\' === o ? n++ : '{' === o ? i++ : '}' === o && i--, n++);
}
return -1;
},
a = /^\\begin{/,
o = function (e, t) {
for (
var n,
i = [],
o = new RegExp(
'(' +
t
.map(function (e) {
return e.left.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
})
.join('|') +
')',
);
-1 !== (n = e.search(o));
) {
n > 0 && (i.push({ type: 'text', data: e.slice(0, n) }), (e = e.slice(n)));
var l = t.findIndex(function (t) {
return e.startsWith(t.left);
});
if (-1 === (n = r(t[l].right, e, t[l].left.length))) break;
var d = e.slice(0, n + t[l].right.length),
s = a.test(d) ? d : e.slice(t[l].left.length, n);
(i.push({ type: 'math', data: s, rawData: d, display: t[l].display }),
(e = e.slice(n + t[l].right.length)));
}
return ('' !== e && i.push({ type: 'text', data: e }), i);
},
l = function (e, r) {
var n = o(e, r.delimiters);
if (1 === n.length && 'text' === n[0].type) return null;
for (var i = document.createDocumentFragment(), a = 0; a < n.length; a++)
if ('text' === n[a].type) i.appendChild(document.createTextNode(n[a].data));
else {
var l = document.createElement('span'),
d = n[a].data;
r.displayMode = n[a].display;
try {
(r.preProcess && (d = r.preProcess(d)), t().render(d, l, r));
} catch (e) {
if (!(e instanceof t().ParseError)) throw e;
(r.errorCallback(
'KaTeX auto-render: Failed to parse `' + n[a].data + '` with ',
e,
),
i.appendChild(document.createTextNode(n[a].rawData)));
continue;
}
i.appendChild(l);
}
return i;
},
d = function e(t, r) {
for (var n = 0; n < t.childNodes.length; n++) {
var i = t.childNodes[n];
if (3 === i.nodeType) {
for (
var a = i.textContent, o = i.nextSibling, d = 0;
o && o.nodeType === Node.TEXT_NODE;
)
((a += o.textContent), (o = o.nextSibling), d++);
var s = l(a, r);
if (s) {
for (var f = 0; f < d; f++) i.nextSibling.remove();
((n += s.childNodes.length - 1), t.replaceChild(s, i));
} else n += d;
} else
1 === i.nodeType &&
(function () {
var t = ' ' + i.className + ' ';
-1 === r.ignoredTags.indexOf(i.nodeName.toLowerCase()) &&
r.ignoredClasses.every(function (e) {
return -1 === t.indexOf(' ' + e + ' ');
}) &&
e(i, r);
})();
}
},
s = function (e, t) {
if (!e) throw new Error('No element provided to render');
var r = {};
for (var n in t) t.hasOwnProperty(n) && (r[n] = t[n]);
((r.delimiters = r.delimiters || [
{ left: '$$', right: '$$', display: !0 },
{ left: '\\(', right: '\\)', display: !1 },
{ left: '\\begin{equation}', right: '\\end{equation}', display: !0 },
{ left: '\\begin{align}', right: '\\end{align}', display: !0 },
{ left: '\\begin{alignat}', right: '\\end{alignat}', display: !0 },
{ left: '\\begin{gather}', right: '\\end{gather}', display: !0 },
{ left: '\\begin{CD}', right: '\\end{CD}', display: !0 },
{ left: '\\[', right: '\\]', display: !0 },
]),
(r.ignoredTags = r.ignoredTags || [
'script',
'noscript',
'style',
'textarea',
'pre',
'code',
'option',
]),
(r.ignoredClasses = r.ignoredClasses || []),
(r.errorCallback = r.errorCallback || console.error),
(r.macros = r.macros || {}),
d(e, r));
};
})(),
(i = i.default)
);
})();
});
+11562
View File
File diff suppressed because it is too large Load Diff
+192132
View File
File diff suppressed because one or more lines are too long
+34
View File
@@ -0,0 +1,34 @@
document.addEventListener('DOMContentLoaded', () => {
const progressBar = document.getElementById('reading-progress-bar');
if (!progressBar) return;
const updateProgress = () => {
// Calculate how far the user has scrolled down the page
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
// Calculate the total scrollable height
const scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
const clientHeight = document.documentElement.clientHeight || window.innerHeight;
const windowHeight = scrollHeight - clientHeight;
// Calculate percentage and update width
const scrollPercentage = windowHeight > 0 ? (scrollTop / windowHeight) * 100 : 0;
progressBar.style.width = scrollPercentage + '%';
};
// Update initially in case the user refreshes midway down the page
updateProgress();
// Throttle the scroll event listener a tiny bit for performance
let isTicking = false;
window.addEventListener('scroll', () => {
if (!isTicking) {
window.requestAnimationFrame(() => {
updateProgress();
isTicking = false;
});
isTicking = true;
}
});
});
+34
View File
@@ -0,0 +1,34 @@
document.addEventListener('DOMContentLoaded', () => {
const scrollToTopBtn = document.getElementById('scroll-to-top');
if (!scrollToTopBtn) return;
// Show button when scrolled down 400px
const toggleVisibility = () => {
if (window.scrollY > 400) {
scrollToTopBtn.classList.add('visible');
} else {
scrollToTopBtn.classList.remove('visible');
}
};
// Smooth scroll to top when clicked
scrollToTopBtn.addEventListener('click', () => {
window.scrollTo({
top: 0,
behavior: 'smooth',
});
});
// Throttle the scroll event listener a tiny bit for performance
let isTicking = false;
window.addEventListener('scroll', () => {
if (!isTicking) {
window.requestAnimationFrame(() => {
toggleVisibility();
isTicking = false;
});
isTicking = true;
}
});
});
+223
View File
@@ -0,0 +1,223 @@
document.addEventListener('DOMContentLoaded', () => {
const searchToggleBtn = document.querySelector('.search-toggle');
if (!searchToggleBtn) return;
// Check if elasticlunr is available
if (typeof elasticlunr === 'undefined') {
console.warn('Elasticlunr not loaded. Search will not work.');
return;
}
// Modal elements
const overlay = document.getElementById('search-overlay');
const input = document.getElementById('search-input');
const resultsContainer = document.getElementById('search-results');
if (!overlay || !input || !resultsContainer) return;
let index = null;
let isIndexLoading = false;
let selectedIndex = -1;
let resultItems = [];
// Initialize search index (loaded lazily on first open)
const initSearch = async () => {
if (index || isIndexLoading) return;
isIndexLoading = true;
try {
// Fetch the Zola-generated index
const response = await fetch('/search_index.en.js');
const jsText = await response.text();
// The file defines `window.searchIndex = {...}`
// We use a safe evaluation approach if it's purely assignment
// Or just append it as a script tag
const script = document.createElement('script');
script.innerHTML = jsText;
document.body.appendChild(script);
// Wait for it to parse
setTimeout(() => {
if (window.searchIndex) {
index = elasticlunr.Index.load(window.searchIndex);
// Search immediately if there's text
if (input.value.trim().length > 0) {
performSearch();
}
}
}, 50);
} catch (e) {
console.error('Failed to load search index:', e);
}
};
// Open Modal
const openSearch = () => {
overlay.classList.add('active');
// Slight delay ensures the browser focuses the input after the display transition starts
setTimeout(() => {
input.focus();
}, 50);
initSearch();
document.body.style.overflow = 'hidden'; // Prevent background scrolling
};
// Close Modal
const closeSearch = () => {
overlay.classList.remove('active');
input.value = '';
resultsContainer.innerHTML = '';
document.body.style.overflow = '';
selectedIndex = -1;
resultItems = [];
};
// Bind Toggle Button
searchToggleBtn.addEventListener('click', (e) => {
e.preventDefault();
if (overlay.classList.contains('active')) {
closeSearch();
} else {
openSearch();
}
});
// Close on overlay click
overlay.addEventListener('click', (e) => {
if (e.target === overlay) closeSearch();
});
// Handle Keyboard Shortcuts
document.addEventListener('keydown', (e) => {
// Ctrl+K -> Open Search
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault();
openSearch();
}
if (!overlay.classList.contains('active')) return;
// Escape -> Close Search
if (e.key === 'Escape') {
closeSearch();
}
// Arrow Navigation
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
e.preventDefault();
if (resultItems.length === 0) return;
if (e.key === 'ArrowDown') {
selectedIndex = (selectedIndex + 1) % resultItems.length;
} else {
selectedIndex = (selectedIndex - 1 + resultItems.length) % resultItems.length;
}
updateSelection();
}
// Enter -> Navigate to selected
if (e.key === 'Enter') {
e.preventDefault();
if (selectedIndex >= 0 && resultItems[selectedIndex]) {
window.location.href = resultItems[selectedIndex].href;
} else if (resultItems.length > 0) {
// Default to first item if none selected
window.location.href = resultItems[0].href;
}
}
});
const updateSelection = () => {
resultItems.forEach((el, i) => {
if (i === selectedIndex) {
el.classList.add('selected');
el.scrollIntoView({ block: 'nearest' });
} else {
el.classList.remove('selected');
}
});
};
// Perform the Search
const performSearch = () => {
if (!index) {
resultsContainer.innerHTML = '<div class="no-results">Loading search index...</div>';
return;
}
const query = input.value.trim();
if (query.length === 0) {
resultsContainer.innerHTML = '';
selectedIndex = -1;
resultItems = [];
return;
}
const options = {
bool: 'OR',
expand: true,
};
const results = index.search(query, options);
if (results.length === 0) {
resultsContainer.innerHTML = `<div class="no-results">No results found for "${query}"</div>`;
selectedIndex = -1;
resultItems = [];
return;
}
// Build HTML
let html = '';
results.forEach((res) => {
// The doc is stored inside the index.documentStore
// For Zola + elasticlunr, res.ref IS the URL.
const url = res.ref;
const doc = index.documentStore.docs[res.ref];
if (!doc) return;
// Truncate summary if needed
let body = doc.body || '';
if (body.length > 150) {
body = body.substring(0, 150) + '...';
}
const title = doc.title || url;
html += `
<a href="${url}" class="search-result-item">
<div class="result-title">${title}</div>
<div class="result-summary">${body}</div>
</a>
`;
});
resultsContainer.innerHTML = html;
// Cache DOM elements for keyboard navigation
resultItems = Array.from(resultsContainer.querySelectorAll('.search-result-item'));
selectedIndex = -1; // Reset selection on new search
// Mouse hover updates selection index
resultItems.forEach((item, i) => {
item.addEventListener('mouseenter', () => {
selectedIndex = i;
updateSelection();
});
});
};
// Debounce the input
let debounceTimer;
input.addEventListener('input', () => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
performSearch();
}, 150);
});
});
+63
View File
@@ -0,0 +1,63 @@
document.addEventListener('DOMContentLoaded', () => {
const themeToggleBtn = document.getElementById('theme-toggle');
const htmlElement = document.documentElement;
const applyTheme = (theme) => {
if (theme === 'dark') {
htmlElement.classList.add('dark');
} else {
htmlElement.classList.remove('dark');
}
localStorage.setItem('theme', theme);
};
// Check local storage for theme preference
const currentTheme = localStorage.getItem('theme');
if (currentTheme === 'dark' || currentTheme === 'light') {
applyTheme(currentTheme);
} else {
// Fallback to system preference
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
applyTheme(prefersDarkScheme.matches ? 'dark' : 'light');
}
// Toggle theme on click
if (themeToggleBtn) {
themeToggleBtn.addEventListener('click', () => {
const nextTheme = htmlElement.classList.contains('dark') ? 'light' : 'dark';
applyTheme(nextTheme);
});
}
// Mobile nav toggle
const navToggle = document.getElementById('nav-toggle');
const navLinks = document.getElementById('nav-links');
if (navToggle && navLinks) {
navToggle.addEventListener('click', () => {
const isOpen = navLinks.classList.toggle('is-open');
navToggle.setAttribute('aria-expanded', isOpen);
});
// Close nav when a link is clicked
navLinks.querySelectorAll('.nav-item').forEach((link) => {
link.addEventListener('click', () => {
navLinks.classList.remove('is-open');
navToggle.setAttribute('aria-expanded', 'false');
});
});
}
// Sync cookieconsent dark mode reactively with the site theme
const syncCCTheme = () => {
const ccMain = document.getElementById('cc-main');
if (!ccMain) return;
ccMain.classList.toggle('cc--darkmode', htmlElement.classList.contains('dark'));
};
new MutationObserver(syncCCTheme).observe(htmlElement, {
attributes: true,
attributeFilter: ['class'],
});
// Initial sync in case banner is already mounted
setTimeout(syncCCTheme, 100);
});
+59
View File
@@ -0,0 +1,59 @@
(function () {
'use strict';
const sidebar = document.querySelector('.post-toc-sidebar');
if (!sidebar) return;
const links = Array.from(sidebar.querySelectorAll('a[data-toc-id]'));
if (!links.length) return;
const headingIds = links.map((a) => a.dataset.tocId);
const headings = headingIds.map((id) => document.getElementById(id)).filter(Boolean);
let activeId = null;
function setActive(id) {
if (id === activeId) return;
activeId = id;
links.forEach((a) => {
if (a.dataset.tocId === id) {
a.classList.add('toc-active');
} else {
a.classList.remove('toc-active');
}
});
}
const observer = new IntersectionObserver(
(entries) => {
// Find the topmost visible heading
const visible = headings.filter((h) => {
const rect = h.getBoundingClientRect();
return rect.top >= 0 && rect.top < window.innerHeight * 0.6;
});
if (visible.length > 0) {
setActive(visible[0].id);
} else {
// If scrolled past all headings, keep last active
const above = headings.filter((h) => h.getBoundingClientRect().top < 0);
if (above.length > 0) {
setActive(above[above.length - 1].id);
}
}
},
{
rootMargin: '-10% 0px -60% 0px',
threshold: 0,
},
);
headings.forEach((h) => observer.observe(h));
// Set initial active on load
const firstVisible = headings.find((h) => {
const rect = h.getBoundingClientRect();
return rect.top >= 0;
});
if (firstVisible) setActive(firstVisible.id);
})();
-1
View File
File diff suppressed because one or more lines are too long
+136
View File
@@ -0,0 +1,136 @@
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:atom="http://www.w3.org/2005/Atom">
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<title><xsl:value-of select="/rss/channel/title"/> - RSS Feed</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<style>
:root {
--bg-color: #f9fafb;
--text-color: #1a1a1a;
--text-muted: #6b7280;
--accent-color: #10b981;
--hover-color: #059669; /* deeper emerald */
--card-bg: #ffffff;
--border-color: rgba(0, 0, 0, 0.05);
}
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #0a0a0a;
--text-color: #f9fafb;
--text-muted: #a1a1aa;
--accent-color: #10b981;
--hover-color: #34d399; /* lighter emerald */
--card-bg: #141414;
--border-color: rgba(255, 255, 255, 0.1);
}
}
body {
font-family: 'Outfit', system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background-color: var(--bg-color);
color: var(--text-color);
line-height: 1.6;
margin: 0;
padding: 2rem;
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
a {
color: var(--accent-color);
text-decoration: none;
transition: color 0.2s ease;
}
a:hover {
color: var(--hover-color);
text-decoration: underline;
}
.header {
margin-bottom: 3rem;
padding-bottom: 2rem;
border-bottom: 2px solid var(--border-color);
}
.header h1 {
font-size: 2.5rem;
margin: 0 0 0.5rem 0;
color: var(--accent-color);
}
.header p {
font-size: 1.2rem;
margin: 0;
opacity: 0.9;
}
.notice {
background-color: var(--card-bg);
padding: 1rem;
border-radius: 8px;
margin-bottom: 2rem;
border-left: 4px solid var(--accent-color);
}
.item {
background-color: var(--card-bg);
padding: 1.5rem;
border-radius: 8px;
margin-bottom: 1.5rem;
border: 1px solid var(--border-color);
}
.item h2 {
margin: 0 0 0.5rem 0;
font-size: 1.5rem;
}
.item .meta {
font-size: 0.9rem;
opacity: 0.7;
margin-bottom: 1rem;
}
.btn {
display: inline-block;
background-color: var(--accent-color);
color: var(--bg-color);
padding: 0.5rem 1rem;
border-radius: 4px;
font-weight: 600;
margin-top: 1rem;
}
.btn:hover {
background-color: var(--hover-color);
color: var(--bg-color);
text-decoration: none;
}
</style>
</head>
<body>
<div class="header">
<h1><xsl:value-of select="/rss/channel/title"/></h1>
<p><xsl:value-of select="/rss/channel/description"/></p>
</div>
<div class="notice">
<strong>This is an RSS feed.</strong> Subscribe by copying the URL from your address bar into your news reader app.
</div>
<div class="items">
<xsl:for-each select="/rss/channel/item">
<div class="item">
<h2>
<a href="{link}">
<xsl:value-of select="title"/>
</a>
</h2>
<div class="meta">
Published: <xsl:value-of select="pubDate"/>
</div>
<div>
<xsl:value-of select="substring(description, 1, 250)"/>...
</div>
<a href="{link}" class="btn">Read article &#x2192;</a>
</div>
</xsl:for-each>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>