Files
kennethreitz.org/templates/base.html
T
2025-09-06 15:01:58 -04:00

1179 lines
41 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{% block title %}{{ title }} - Kenneth Reitz{% endblock %}</title>
<!-- SEO and Meta -->
<meta name="description" content="{% block description %}Kenneth Reitz - Creator of Requests and Certifi, trusted by millions of developers worldwide. Thoughts on technology, philosophy, and building software for humans.{% endblock %}" />
<meta name="author" content="Kenneth Reitz" />
<!-- OpenGraph -->
<meta property="og:title" content="{% block og_title %}{{ title }} - Kenneth Reitz{% endblock %}" />
<meta property="og:description" content="{% block og_description %}Creator of Requests and Certifi - libraries trusted by millions of developers worldwide{% endblock %}" />
<meta property="og:type" content="{% block og_type %}website{% endblock %}" />
<meta property="og:url" content="{% block og_url %}https://kennethreitz.org{% endblock %}" />
<meta property="og:image" content="{% block og_image %}https://kennethreitz.org/static/images/social-card.jpg{% endblock %}" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:site_name" content="Kenneth Reitz" />
<!-- Twitter Card -->
<meta name="twitter:card" content="{% block twitter_card %}summary_large_image{% endblock %}" />
<meta name="twitter:creator" content="@kennethreitz42" />
<meta name="twitter:title" content="{% block twitter_title %}{{ title }} - Kenneth Reitz{% endblock %}" />
<meta name="twitter:description" content="{% block twitter_description %}Creator of Requests and Certifi - libraries trusted by millions of developers worldwide{% endblock %}" />
<meta name="twitter:image" content="{% block twitter_image %}https://kennethreitz.org/static/images/social-card.jpg{% endblock %}" />
<!-- RSS Feed -->
<link rel="alternate" type="application/rss+xml" title="Kenneth Reitz - Essays &amp; AI Writings" href="/feed.xml" />
<!-- Tufte CSS -->
<link rel="stylesheet" href="/static/tufte/tufte.css" />
<style>
/* Fix for ET Book font bold-italic rendering issues */
/* ET Book doesn't have a true bold-italic variant, so we disable faux bold */
strong em, em strong, b i, i b,
.subtitle strong em, .subtitle em strong,
.subtitle b i, .subtitle i b,
nav strong em, nav em strong,
h1 strong em, h1 em strong,
h2 strong em, h2 em strong,
h3 strong em, h3 em strong {
font-weight: normal !important;
font-style: italic !important;
}
/* For better readability, you could also use the semi-bold variant */
/* which ET Book does support, though still no bold-italic */
.subtitle em, .subtitle i,
nav em, nav i {
font-weight: 500; /* semi-bold instead of bold for italics */
}
/* Adjust content width for better sidenote spacing */
section > p,
section > footer,
section > table,
section > blockquote,
p,
blockquote,
.post-content > p,
.post-content > blockquote,
.post-content > ul,
.post-content > ol {
width: 65% !important;
}
section > dl,
section > ol,
section > ul,
.post-content > dl {
width: 65%;
}
/* Override for full-width elements when needed */
.post-content > h1,
.post-content > h2,
.post-content > h3,
.post-content > h4,
.post-content > h5,
.post-content > h6 {
width: 100%;
}
.sidenote,
.marginnote,
section .sidenote,
section .marginnote,
article .sidenote,
article .marginnote {
width: 35% !important;
margin-right: -39% !important;
margin-left: 6% !important;
overflow-wrap: break-word !important;
word-wrap: break-word !important;
}
/* Responsive adjustments */
@media (max-width: 1400px) {
section > p,
section > footer,
section > table,
section > blockquote,
p,
blockquote,
.post-content > p,
.post-content > blockquote,
.post-content > ul,
.post-content > ol {
width: 80%;
}
.sidenote,
.marginnote,
section .sidenote,
section .marginnote,
article .sidenote,
article .marginnote {
width: 32% !important;
margin-right: -36% !important;
margin-left: 6% !important;
}
}
@media (max-width: 1200px) {
section > p,
section > footer,
section > table,
section > blockquote,
p,
blockquote,
.post-content > p,
.post-content > blockquote,
.post-content > ul,
.post-content > ol {
width: 80%;
}
.sidenote,
.marginnote,
section .sidenote,
section .marginnote,
article .sidenote,
article .marginnote {
width: 30% !important;
margin-right: -34% !important;
margin-left: 6% !important;
}
}
/* Code blocks - extend wider but respect content margin */
section > pre,
.post-content > pre,
.tufte pre,
pre {
width: calc(90vw - 2rem) !important;
max-width: 1400px !important;
margin-left: 0 !important;
margin-right: auto !important;
position: relative;
box-sizing: border-box;
}
/* Override Tufte CSS constraint on code within pre */
pre > code {
width: 100% !important;
max-width: none !important;
}
/* Copy button for code blocks */
.code-block-wrapper {
position: relative;
}
.copy-button {
position: absolute;
top: 8px;
right: 8px;
background: rgba(255, 255, 255, 0.9);
border: 1px solid #ddd;
border-radius: 4px;
padding: 4px 8px;
font-size: 12px;
cursor: pointer;
opacity: 0;
transition: opacity 0.2s ease;
z-index: 10;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
}
.code-block-wrapper:hover .copy-button,
.copy-button:focus {
opacity: 1;
}
.copy-button:hover {
background: rgba(255, 255, 255, 1);
border-color: #999;
}
.copy-button:active {
background: rgba(240, 240, 240, 1);
}
.copy-button.copied {
background: rgba(76, 175, 80, 0.9);
color: white;
border-color: #4CAF50;
}
@media (prefers-color-scheme: dark) {
.copy-button {
background: rgba(50, 50, 50, 0.9);
color: white;
border-color: #555;
}
.copy-button:hover {
background: rgba(70, 70, 70, 1);
border-color: #777;
}
.copy-button:active {
background: rgba(30, 30, 30, 1);
}
}
/* Responsive adjustments for code blocks */
@media (max-width: 768px) {
section > pre,
.post-content > pre,
.tufte pre,
pre {
width: calc(100vw - 2rem) !important;
margin-left: -1rem !important;
}
}
/* Breadcrumb styling */
.breadcrumbs {
font-size: 0.875rem;
color: #666;
margin: 1rem 0 2rem 0;
font-family: et-book, Palatino, "Palatino Linotype", "Palatino LT STD", "Book Antiqua", Georgia, serif;
}
.breadcrumbs a {
color: #666;
text-decoration: none;
background: none;
text-shadow: none;
}
.breadcrumbs a:hover {
color: #111;
}
.breadcrumb-separator {
margin: 0 0.5rem;
color: #999;
}
/* Mobile breadcrumb adjustments */
@media (max-width: 760px) {
.breadcrumbs {
font-size: 0.8rem;
margin: 0.75rem 0 1.5rem 0;
}
.breadcrumb-separator {
margin: 0 0.25rem;
}
}
/* Homepage subtitle spacing adjustment */
p.subtitle {
line-height: 1.6;
}
/* Navigation spacing adjustment */
header nav a {
margin-right: 1rem;
}
header nav a:last-child {
margin-right: 0;
}
/* Random post button styling */
.random-link {
font-size: 1.1rem;
}
/* Directory listing styling */
.directory-listing ul {
list-style: none;
padding-left: 0;
font-size: 0.95rem;
line-height: 1.8;
}
.directory-listing li {
margin-bottom: 0.75rem;
padding-left: 2rem;
position: relative;
font-family: et-book, Palatino, "Palatino Linotype", "Palatino LT STD", "Book Antiqua", Georgia, serif;
}
.directory-listing li::before {
content: attr(data-icon);
position: absolute;
left: 0;
color: #666;
font-size: 0.9rem;
}
.directory-listing a {
color: #444;
text-decoration: none;
font-weight: 400;
background: none;
text-shadow: none;
transition: color 0.2s ease;
}
.directory-listing a:hover {
color: #111;
}
.item-date {
font-size: 0.8rem;
color: #888;
font-style: italic;
margin-left: 0.75rem;
font-variant: small-caps;
letter-spacing: 0.02em;
}
.directory-listing h3 {
font-size: 1.3rem;
color: #333;
font-weight: 400;
font-style: italic;
margin-bottom: 1.5rem;
margin-top: 2.5rem;
}
/* Hide HR lines to prevent sidenote interference */
hr {
display: none;
}
/* Fix iframe overflow into sidenotes */
iframe {
max-width: 55% !important;
width: 100%;
box-sizing: border-box;
}
@media (max-width: 1400px) {
iframe {
max-width: 60% !important;
}
}
@media (max-width: 1200px) {
iframe {
max-width: 70% !important;
}
}
@media (max-width: 760px) {
iframe {
max-width: 100% !important;
}
}
/* Footer spacing */
.footer-note {
margin-top: 3rem;
}
/* Make article paragraphs slightly smaller */
.post-content p:not(.sidenote):not(.marginnote) {
font-size: 1.3rem !important;
width: 70% !important;
}
/* Make lists wider for better readability */
.post-content > ul,
.post-content > ol {
width: 72.5% !important;
}
@media (max-width: 1400px) {
.post-content > ul,
.post-content > ol {
width: 72.5% !important;
}
}
@media (max-width: 1200px) {
.post-content > ul,
.post-content > ol {
width: 72.5% !important;
}
}
/* Make blockquote paragraphs wider */
.post-content blockquote p,
article .post-content blockquote p,
section .post-content blockquote p,
blockquote .post-content p,
blockquote p,
blockquote,
.post-content blockquote,
article blockquote,
section blockquote,
main blockquote,
body blockquote {
width: 75% !important;
}
/* Make list item paragraphs wider */
ol li p,
ul li p,
.post-content ol li p,
.post-content ul li p {
width: 75% !important;
}
/* Adjust heading sizes */
h2 {
font-size: 2.1rem !important;
}
h3 {
font-size: 1.7rem !important;
font-weight: 500 !important;
}
/* Reduce article padding */
article {
padding: 3rem 0rem !important;
}
/* === PREMIUM TYPOGRAPHY ENHANCEMENTS === */
/* Advanced font rendering */
html {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
body {
font-kerning: normal;
text-rendering: optimizeLegibility;
font-weight: 420;
}
/* Enhanced link rendering */
a {
text-decoration-skip-ink: auto;
}
/* Beautiful text selection */
::selection {
background: rgba(0, 0, 0, 0.1);
color: inherit;
text-shadow: none;
}
::-moz-selection {
background: rgba(0, 0, 0, 0.1);
color: inherit;
text-shadow: none;
}
/* === SUBTLE CODE SYNTAX HIGHLIGHTING === */
/* Make comments lighter in code blocks */
pre code .hljs-comment,
pre .hljs-comment,
code .comment,
pre .comment {
color: #888 !important;
font-style: italic;
}
/* Handle various comment patterns */
pre code:has-text("//"),
pre code:has-text("#"),
pre code:has-text("/*"),
pre code:has-text("<!--") {
/* Base styling handled by specific selectors below */
}
/* Simple regex-based comment highlighting for common patterns */
pre {
/* This will be handled by JavaScript for dynamic highlighting */
}
/* Fix excessive right padding on mobile */
@media (max-width: 760px) {
.post-content p:not(.sidenote):not(.marginnote) {
width: 100% !important;
}
body {
padding-left: 8% !important;
padding-right: 8% !important;
width: 84% !important;
}
/* Hide sidenotes and their labels on narrow screens */
.sidenote,
.marginnote {
display: none;
}
.margin-toggle,
label.margin-toggle {
display: none !important;
}
/* Make content full width when sidenotes are hidden */
section > p,
section > footer,
section > table,
section > blockquote,
p,
blockquote,
.post-content > p,
.post-content > blockquote,
.post-content > ul,
.post-content > ol {
width: 100%;
}
section > dl,
section > ol,
section > ul,
.post-content > dl {
width: 100%;
}
}
/* Mobile font fixes */
@media (max-width: 760px) {
body, p, h1, h2, h3, h4, h5, h6 {
text-shadow: none !important;
-webkit-text-stroke: 0 !important;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
/* Fix Tufte's complex link underlines on mobile */
a, a:link, a:visited,
.tufte-underline,
.hover-tufte-underline:hover {
text-shadow: none !important;
background: none !important;
text-decoration: underline !important;
text-decoration-thickness: 1px !important;
text-underline-offset: 2px !important;
}
/* Ensure no double font rendering */
* {
-webkit-transform: translateZ(0);
transform: translateZ(0);
}
}
/* Print Styles for Tufte CSS */
@media print {
* {
background: transparent !important;
color: black !important;
box-shadow: none !important;
text-shadow: none !important;
}
body {
font-size: 12pt;
line-height: 1.3;
margin: 0;
padding: 0;
background: white;
color: black;
width: 100% !important;
max-width: none !important;
}
/* Hide navigation and non-essential elements */
header nav,
.breadcrumbs,
.post-navigation,
.related-posts,
.footer-content,
.search-container {
display: none !important;
}
/* Optimize content for print */
section > p,
section > footer,
section > table,
section > blockquote,
p,
blockquote,
.post-content > p,
.post-content > blockquote,
.post-content > ul,
.post-content > ol {
width: 100% !important;
max-width: none !important;
}
section > dl,
section > ol,
section > ul,
.post-content > dl {
width: 100% !important;
}
/* Convert sidenotes to footnotes for print */
.sidenote,
.marginnote {
display: none !important;
}
/* Show sidenote content as footnotes at bottom */
.sidenote::before {
content: "["counter(sidenote-counter) "] ";
counter-increment: sidenote-counter;
}
/* Print-specific typography */
h1 {
font-size: 18pt;
margin: 24pt 0 12pt 0;
page-break-after: avoid;
}
h2 {
font-size: 16pt;
margin: 20pt 0 10pt 0;
page-break-after: avoid;
}
h3 {
font-size: 14pt;
margin: 16pt 0 8pt 0;
page-break-after: avoid;
}
p {
font-size: 12pt;
margin: 12pt 0;
text-align: justify;
orphans: 3;
widows: 3;
}
/* Print links with URLs */
a[href]:after {
content: " (" attr(href) ")";
font-size: 10pt;
color: #666;
}
a[href^="#"]:after,
a[href^="/"]:after {
content: "";
}
/* Blockquotes */
blockquote {
margin: 12pt 24pt;
padding: 0;
font-style: italic;
border-left: 2pt solid #ccc;
padding-left: 12pt;
}
/* Code blocks */
pre, code {
font-family: "Courier New", Courier, monospace;
font-size: 10pt;
background: #f5f5f5;
border: 1pt solid #ccc;
padding: 6pt;
}
pre {
white-space: pre-wrap;
page-break-inside: avoid;
}
/* Override width constraints for code blocks */
section > pre,
.post-content > pre,
pre {
width: 85% !important;
max-width: none !important;
margin-left: 0 !important;
}
/* Tables */
table {
border-collapse: collapse;
width: 100%;
font-size: 10pt;
}
th, td {
border: 1pt solid #ccc;
padding: 6pt;
text-align: left;
}
th {
background: #f0f0f0;
font-weight: bold;
}
/* Images */
img {
max-width: 100%;
height: auto;
page-break-inside: avoid;
}
/* Page breaks */
.page-break {
page-break-before: always;
}
/* Hide toggle inputs for sidenotes */
input[type="checkbox"].margin-toggle {
display: none !important;
}
label.margin-toggle {
display: none !important;
}
/* Header styling for print */
header h1 {
font-size: 20pt;
margin-bottom: 6pt;
}
.subtitle {
font-size: 14pt;
margin-bottom: 20pt;
font-style: italic;
}
/* Ensure proper spacing */
section {
margin-bottom: 20pt;
}
/* Lists */
ul, ol {
margin: 12pt 0;
padding-left: 20pt;
}
li {
margin: 6pt 0;
}
}
/* Heading anchor links */
h1, h2, h3, h4, h5, h6 {
position: relative;
}
.heading-anchor {
position: absolute;
left: -1.2em;
top: 0;
opacity: 0;
color: #ccc;
text-decoration: none;
font-weight: normal;
font-style: italic;
cursor: pointer;
user-select: none;
padding: 0 0.2em;
}
.heading-anchor:hover {
color: #999;
text-decoration: none;
}
h1:hover .heading-anchor,
h2:hover .heading-anchor,
h3:hover .heading-anchor,
h4:hover .heading-anchor,
h5:hover .heading-anchor,
h6:hover .heading-anchor {
opacity: 1;
}
/* Mobile adjustments for anchor links */
@media (max-width: 768px) {
.heading-anchor {
left: -0.8em;
font-size: 0.9em;
}
}
</style>
<!-- Structured Data (JSON-LD) -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "{% block schema_type %}WebSite{% endblock %}",
"name": "{% block schema_name %}Kenneth Reitz{% endblock %}",
"url": "https://kennethreitz.org{% block schema_url %}/{% endblock %}",
"description": "{% block schema_description %}Kenneth Reitz - Creator of Requests and Certifi, trusted by millions of developers worldwide. Thoughts on technology, philosophy, and building software for humans.{% endblock %}",
"author": {
"@type": "Person",
"name": "Kenneth Reitz",
"url": "https://kennethreitz.org",
"sameAs": [
"https://github.com/kennethreitz",
"https://twitter.com/kennethreitz42"
],
"jobTitle": "Python Developer",
"worksFor": {
"@type": "Organization",
"name": "Independent"
},
"knowsAbout": [
"Python Programming",
"API Design",
"Software Architecture",
"Open Source Development",
"Artificial Intelligence",
"Mental Health Advocacy"
]
},
"publisher": {
"@type": "Person",
"name": "Kenneth Reitz",
"url": "https://kennethreitz.org"
}{% block schema_extra %}{% endblock %}
}
</script>
{% block extra_head %}{% endblock %}
<!-- Analytics -->
{% if not (config.get('DISABLE_ANALYTICS') or request.environ.get('DISABLE_ANALYTICS')) %}
<script type="text/javascript">
var _gauges = _gauges || [];
(function() {
var t = document.createElement('script');
t.type = 'text/javascript';
t.async = true;
t.id = 'gauges-tracker';
t.setAttribute('data-site-id', '65529a9abd1a3b3101979d52');
t.setAttribute('data-track-path', 'https://track.gaug.es/track.gif');
t.src = 'https://d2fuc4clr7gvcn.cloudfront.net/track.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(t, s);
})();
</script>
{% endif %}
</head>
<body>
<article>
<header>
<nav>
<a href="/">Home</a>
<a href="/archive">Archive</a>
<a href="/themes">Themes</a>
<a href="/directory">Directory</a>
<a href="/search">[search]</a>
<a href="/random" class="random-link">[random]</a>
</nav>
{% if current_path or breadcrumbs %}
<div class="breadcrumbs">
<a href="/">Home</a>
{% if breadcrumbs %}
{% for crumb in breadcrumbs %}
<span class="breadcrumb-separator"></span>
<a href="{{ crumb.url }}">{{ crumb.name }}</a>
{% endfor %}
{% elif current_path %}
{% set path_parts = current_path.strip('/').split('/') %}
{% for part in path_parts if part %}
<span class="breadcrumb-separator"></span>
{% set partial_path = '/' + path_parts[:loop.index]|join('/') %}
<a href="{{ partial_path }}">{{ part }}</a>
{% endfor %}
{% endif %}
</div>
{% endif %}
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<div class="footer-content">
<div class="footer-note">
<p>&copy; {{ "now"|strftime("%Y") }} Kenneth Reitz. Made with <a href="https://kjvstudy.org/book/1%20Corinthians/chapter/13">love</a>.</p>
</div>
</div>
</footer>
</article>
{% block extra_scripts %}{% endblock %}
<!-- Force light mode, especially on mobile -->
<style>
@media (prefers-color-scheme: dark) {
body {
background-color: rgb(255, 255, 248) !important;
color: #111 !important;
}
h1, h2, h3, h4, h5, h6 {
color: #333 !important;
}
a {
color: #111 !important;
}
/* Force light backgrounds on all elements */
* {
background-color: transparent !important;
color: inherit !important;
}
/* Override any dark mode styling */
body, html {
background: rgb(255, 255, 248) !important;
color: #111 !important;
}
}
/* Mobile-specific light mode enforcement */
@media (max-width: 760px) {
body {
background-color: rgb(255, 255, 248) !important;
color: #111 !important;
}
* {
background-color: transparent !important;
}
body, html {
background: rgb(255, 255, 248) !important;
color: #111 !important;
}
}
</style>
<!-- Simplified Color System (light themes only) -->
<script>
(function() {
// Check if user prefers light mode or is on mobile
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
const isMobile = window.innerWidth <= 760;
// Force light mode on mobile or if user prefers dark (override their dark preference)
if (isMobile || prefersDark) {
document.body.style.backgroundColor = 'rgb(255, 255, 248)';
document.body.style.color = '#111';
return; // Skip color schemes entirely
}
const lightColorSchemes = [
'scheme-ocean',
'scheme-forest',
'scheme-sunset',
'scheme-lavender',
'scheme-rose',
'scheme-sage',
'scheme-amber'
];
// Get or generate a scheme based on the current page
let scheme = localStorage.getItem('current-color-scheme');
// Change scheme occasionally (20% chance on page load)
if (!scheme || Math.random() < 0.2) {
scheme = lightColorSchemes[Math.floor(Math.random() * lightColorSchemes.length)];
localStorage.setItem('current-color-scheme', scheme);
}
// Apply the scheme
document.body.className = (document.body.className + ' ' + scheme).trim();
})();
</script>
<script>
// Add copy buttons to code blocks and subtle syntax highlighting
document.addEventListener('DOMContentLoaded', function() {
// Find all pre elements (code blocks)
const codeBlocks = document.querySelectorAll('pre');
// Function to add subtle comment highlighting
function highlightComments(pre) {
const code = pre.querySelector('code') || pre;
let html = code.innerHTML;
// Python/Shell comments (# comment) - both line start and inline
html = html.replace(/(^|\s+)(#.*)$/gm, '$1<span style="color: #888; font-style: italic;">$2</span>');
// Python docstrings (""" or ''')
html = html.replace(/("""[\s\S]*?""")/g, '<span style="color: #888; font-style: italic;">$1</span>');
html = html.replace(/('''[\s\S]*?''')/g, '<span style="color: #888; font-style: italic;">$1</span>');
// Python class names (class ClassName)
html = html.replace(/(\bclass\s+)([A-Za-z_][A-Za-z0-9_]*)/g, '$1<span style="font-weight: bold;">$2</span>');
// JavaScript/C++/Java comments (// comment)
html = html.replace(/(\s*\/\/.*)$/gm, '<span style="color: #888; font-style: italic;">$1</span>');
// CSS/C/Java block comments (/* comment */)
html = html.replace(/(\/\*[\s\S]*?\*\/)/g, '<span style="color: #888; font-style: italic;">$1</span>');
// HTML comments (<!-- comment -->)
html = html.replace(/(&lt;!--[\s\S]*?--&gt;)/g, '<span style="color: #888; font-style: italic;">$1</span>');
code.innerHTML = html;
}
codeBlocks.forEach(function(pre) {
// Add subtle comment highlighting first
highlightComments(pre);
// Skip if already wrapped
if (pre.parentElement.classList.contains('code-block-wrapper')) {
return;
}
// Create wrapper div
const wrapper = document.createElement('div');
wrapper.className = 'code-block-wrapper';
// Create copy button
const copyButton = document.createElement('button');
copyButton.className = 'copy-button';
copyButton.textContent = 'Copy';
copyButton.setAttribute('aria-label', 'Copy code to clipboard');
// Add click handler
copyButton.addEventListener('click', async function() {
try {
// Get the text content of the pre element
const codeText = pre.textContent || pre.innerText;
// Use modern clipboard API if available
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(codeText);
} else {
// Fallback for older browsers
const textArea = document.createElement('textarea');
textArea.value = codeText;
textArea.style.position = 'fixed';
textArea.style.left = '-999999px';
textArea.style.top = '-999999px';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
document.execCommand('copy');
textArea.remove();
}
// Show feedback
const originalText = copyButton.textContent;
copyButton.textContent = 'Copied!';
copyButton.classList.add('copied');
setTimeout(function() {
copyButton.textContent = originalText;
copyButton.classList.remove('copied');
}, 2000);
} catch (err) {
console.error('Failed to copy code: ', err);
// Show error feedback
const originalText = copyButton.textContent;
copyButton.textContent = 'Failed';
setTimeout(function() {
copyButton.textContent = originalText;
}, 2000);
}
});
// Wrap the pre element and add the button
pre.parentNode.insertBefore(wrapper, pre);
wrapper.appendChild(pre);
wrapper.appendChild(copyButton);
});
});
// Add heading anchor links
document.addEventListener('DOMContentLoaded', function() {
const headings = document.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]');
headings.forEach(function(heading) {
const anchor = document.createElement('a');
anchor.className = 'heading-anchor';
anchor.innerHTML = '#';
anchor.href = '#' + heading.id;
anchor.title = 'Copy link to this heading';
// Handle click to copy URL with anchor
anchor.addEventListener('click', function(e) {
e.preventDefault();
const url = window.location.protocol + '//' + window.location.host + window.location.pathname + '#' + heading.id;
// Copy to clipboard
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(url).then(function() {
// Briefly show feedback
const originalText = anchor.innerHTML;
anchor.innerHTML = '✓';
anchor.style.color = '#4CAF50';
setTimeout(function() {
anchor.innerHTML = originalText;
anchor.style.color = '';
}, 1000);
});
} else {
// Fallback for older browsers
const textArea = document.createElement('textarea');
textArea.value = url;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
// Show feedback
const originalText = anchor.innerHTML;
anchor.innerHTML = '✓';
anchor.style.color = '#4CAF50';
setTimeout(function() {
anchor.innerHTML = originalText;
anchor.style.color = '';
}, 1000);
}
// Also update the URL in the browser
window.history.replaceState({}, '', url);
});
heading.insertBefore(anchor, heading.firstChild);
});
});
</script>
</body>
</html>