feat: initial commit

This commit is contained in:
Cory Dransfeldt 2024-11-16 16:43:07 -08:00
commit 662a249ad3
No known key found for this signature in database
192 changed files with 24641 additions and 0 deletions

45
public/_headers Normal file
View file

@ -0,0 +1,45 @@
/feeds/all.xml
Content-Type: application/xml; charset=utf-8
x-content-type-options: nosniff
/feeds/books.xml
Content-Type: application/xml; charset=utf-8
x-content-type-options: nosniff
/feeds/links.xml
Content-Type: application/xml; charset=utf-8
x-content-type-options: nosniff
/feeds/posts.xml
Content-Type: application/xml; charset=utf-8
x-content-type-options: nosniff
/feeds/movies.xml
Content-Type: application/xml; charset=utf-8
x-content-type-options: nosniff
/feeds/syndication.xml
Content-Type: application/xml; charset=utf-8
x-content-type-options: nosniff
/api/search
Content-Type: application/json
/.well-known/webfinger
Content-Type: application/jrd+json; charset=utf-8
/blogroll.opml
Content-Disposition: attachment; filename=cory-dransfeldt-blogroll.opml
/music/releases.ics
Content-Type: text/calendar
Cache-Control: public, max-age=0, must-revalidate
Access-Control-Allow-Origin: *
Content-Disposition: inline; filename="releases.ics"
/*
Content-Security-Policy: upgrade-insecure-requests; block-all-mixed-content;
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin, no-referrer-when-downgrade
Permissions-Policy: autoplay=(), camera=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), publickey-credentials-get=()

83
public/_redirects Normal file
View file

@ -0,0 +1,83 @@
# 404s
/now.html /now 301
/contact.html /contact 301
# feeds
/feed.xml /feeds/posts 301
/follow.xml /feeds/all 301
/feeds/posts /feeds/posts.xml 301
/feeds/posts.json /feeds/posts.xml 301
/feeds/links /feeds/links.xml 301
/feeds/links.json /feeds/links.xml 301
/feeds/books /feeds/books.xml 301
/feeds/books.json /feeds/books.xml 301
/feeds/movies /feeds/movies.xml 301
/feeds/movies.json /feeds/movies.xml 301
/feeds/all /feeds/all.xml 301
/feeds/all.json /feeds/all.xml 301
/feeds/posts/ /feeds/posts 301
/feeds/links/ /feeds/links 301
/feeds/books/ /feeds/books 301
/feeds/movies/ /feeds/movies 301
/feeds/all/ /feeds/all 301
# general
/articles/ /posts/ 301
/tags /search 301
/sitemap.xml /sitemap-index.xml 301
/watching/favorite-movies /watching/favorites/movies 301
/watching/favorite-movies/ /watching/favorites/movies 301
/watching/favorite-shows /watching/favorites/shows 301
/watching/favorite-shows/ /watching/favorites/shows 301
# blog posts
/blog/fixing-safari-icloud-syncing /posts/2022/fixing-safari-icloud-syncing/ 301
/blog/migrating-to-fastmail /posts/2022/migrating-to-fastmail/ 301
/blog/client-side-webmentions-in-nextjs /posts/2023/client-side-webmentions-in-nextjs/ 301
/blog/apple-centric-digital-privacy-tools /posts/2022/apple-centric-digital-privacy-tools/ 301
/blog/automating-rss-syndication-with-nextjs-github /posts/2023/automating-rss-syndication-with-nextjs-github/ 301
/blog/apple-music-a-tale-of-woe /posts/2021/apple-music-a-tale-of-woe/ 301
/blog/adding-client-side-rendered-webmentions-to-my-blog /posts/2023/client-side-webmentions-in-nextjs/ 301
/blog/automating-email-cleanup-in-gmail /posts/2022/automating-email-cleanup-in-gmail/ 301
/blog/fastmail-handling-inbound-email-with-regex-filters-now-with-chatgpt /posts/2023/fastmail-handling-inbound-email-with-regex-filters-now-with-chatgpt/ 301
/blog/simple-api-fetch-hooks-with-swr /posts/2022/simple-api-fetch-hooks-with-swr/ 301
/2023/02/automatingandprobablyoverengineeringmy-nowpage /posts/2023/automating-and-overengineering-my-now-page/ 301
/2023/01/workflows-handling-inbound-email-on-fastmail-with-regular-expressions /posts/2023/fastmail-handling-inbound-email-with-regex-filters-now-with-chatgpt/ 301
/posts/2023/i-block-ads/null /posts/2023/i-block-ads/ 301
/posts/2023/i-dont-want-streaming-music/null /posts/2023/i-dont-want-streaming-music/ 301
/posts/2022/migrating-to-fastmail/null /posts/2022/migrating-to-fastmail/ 301
/posts/2023/webmentions-in-eleventy/null /posts/2023/webmentions-in-eleventy/ 301
/posts/2023/a-safari-specific-guide-to-making-the-modern-web-suck-less/null /posts/2023/a-safari-specific-guide-to-making-the-modern-web-suck-less/ 301
/posts/2024/dont-be-afraid-to-admit-when-you-dont-know-something/ /posts/2024/dont-be-afraid-to-admin-when-you-dont-know-something/ 301
/posts/2024/data-sharing-should-always-be-opt-in/ /posts/2024/access-to-data-isnt-a-grant-to-exploit-it/ 301
/posts/2023/popular-posts-widget-using-eleventy-plausible/ /posts/2023/building-a-popular-posts-widget-in-eleventy-using-plausible-analytics/ 301
/posts/2023/i-dont-want-streaming-music/ /posts/2023/i-dont-want-streaming-music-i-just-want-to-stream-my-music/ 301
/posts/2021/apple-music-a-tale-of-woe/ /posts/2022/apple-music-a-tale-of-woe/ 301
/posts/2024/weaving-music-in-and-out-of-my-personal-site/ /posts/2024/weaving-music-data-in-and-out-of-my-personal-website/ 301
/posts/2023/fastmail-handling-inbound-email-with-regex-filters-now-with-chatgpt/ /posts/2023/workflows-handling-inbound-email-on-fastmail-with-regular-expressions-now-featuring-chatgpt/ 301
/-want-anything-your-ai-generates/ /posts/2024/i-dont-want-anything-your-ai-generates/ 301
/posts/2023/default-apps-2023/ /posts/2023/my-default-apps-2023-edition/ 301
/posts/2024/dont-be-afraid-to-admin-when-you-dont-know-something/ /posts/2024/dont-be-afraid-to-admin-when-you-dont-know-something/ 301
/posts/2023/displaying-listening-data-from-apple-music-using-musickit/ /posts/2023/displaying-listening-data-from-apple-music-using-musickitjs/ 301
/posts/2024/2024-minimalism-as-self-preservation/ /posts/2024/minimalism-as-self-preservation/ 301
/posts/2023/client-side-webmentions-in-nextjs/ /posts/2023/adding-client-side-webmentions-to-my-nextjs-blog/ 301
/posts/2024/2024-adblocker-required/ /posts/2024/adblocker-required/ 301
/posts/2023/scheduled-eleventy-builds-cron-github-actions/ /posts/2023/scheduled-eleventy-builds-on-vercel-with-cron-triggered-github-actions/ 301
/posts/2024/against-the-commercial-web/ /posts/2024/against-the-commercial-internet/ 301
/posts/2023/i-removed-tailwind-from-my-site/ /posts/2024/i-removed-tailwind-from-my-site/
/posts/2024/handling-images-with-b2-netlify-image-cdn-hazel-mountain-duck/ /posts/2024/handling-images-with-b2-netlifys-image-cdn-hazel-and-mountain-duck/ 301
/posts/2023/my-default-apps-2023-edition/ /uses 301
/posts/2024/link-blogging-using-readwise/ /posts/2024/link-blogging-using-readwise-reader/ 301
/2022/12/automating-email-cleanup-in-gmail /posts/2022/automating-email-cleanup-in-gmail/ 301
/posts/2023/automate-syndicate-content-mastodon-eleventy/ /posts/2023/automate-and-syndicate-content-from-eleventy-to-mastodon/ 301
/posts/2023/road-to-madness-apple-music-charts/ /posts/2023/road-to-madness-charting-apple-music-listening-data/ 301
/posts/2023/semi-automated-hashtags-syndicated-posts/ /posts/2023/semi-automated-hashtags-for-syndicated-posts/ 301
/posts/2023/automating-rss-syndication-with-nextjs-github/ /posts/2023/automating-rss-syndication-and-sharing-with-nextjs-and-github/ 301
/posts/2023/locally-stored-music-and-storage-as-a-meaningful-constraint/ /posts/2023/doppler-locally-stored-music-and-storage-as-a-beneficial-constraint/ 301
/blog/digital-privacy-tools /posts/2021/digital-privacy-tools/ 301
/posts/2023/now-page-update-matter-favorites/ /posts/2023/now-page-update-favorite-articles-from-matter/ 301
/posts/2023/now-playing-eleventy-netlify-edge-functions-emoji/ /posts/2023/displaying-now-playing-data-with-matching-emoji-using-netlify-edge-functions-and-eleventy/ 301
/posts/2014/sublime-text-ctrl-tab-key-bindings/ /posts/2014/sublime-text-3-ctrl-tab-key-bindings/ 301
/posts/2022/simple-api-fetch-hooks-with-swr/ /posts/2022/simple-data-fetching-with-custom-react-hooks-and-swr/ 301
/posts/2023/drying-up-now-page-templates-eleventy/ /posts/2023/drying-up-now-page-templates-and-normalizing-data-in-eleventy/ 301

5340
public/feeds/style.css Normal file

File diff suppressed because it is too large Load diff

78
public/feeds/style.xsl Normal file
View file

@ -0,0 +1,78 @@
<?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" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">
<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" /> Web Feed</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<link rel="stylesheet" href="/feeds/style.css" type="text/css" />
</head>
<body class="bg-white">
<nav class="container-md px-3 py-2 mt-2 mt-md-5 mb-5 markdown-body">
<p class="bg-yellow-light ml-n1 px-1 py-1 mb-1">
<strong>This is a web feed,</strong> also known as an RSS feed. <strong>Subscribe</strong>
by copying the URL from the address bar into your newsreader. </p>
<p class="text-gray"> Visit <a href="https://aboutfeeds.com">About Feeds</a> to get
started with newsreaders and subscribing. Its free. </p>
</nav>
<div class="container-md px-3 py-3 markdown-body">
<header class="py-5">
<h1 class="border-0">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
style="vertical-align: text-bottom; width: 1.2em; height: 1.2em;" class="pr-1"
id="RSSicon" viewBox="0 0 256 256">
<defs>
<linearGradient x1="0.085" y1="0.085" x2="0.915" y2="0.915" id="RSSg">
<stop offset="0.0" stop-color="#E3702D" />
<stop offset="0.1071" stop-color="#EA7D31" />
<stop offset="0.3503" stop-color="#F69537" />
<stop offset="0.5" stop-color="#FB9E3A" />
<stop offset="0.7016" stop-color="#EA7C31" />
<stop offset="0.8866" stop-color="#DE642B" />
<stop offset="1.0" stop-color="#D95B29" />
</linearGradient>
</defs>
<rect width="256" height="256" rx="55" ry="55" x="0" y="0" fill="#CC5D15" />
<rect width="246" height="246" rx="50" ry="50" x="5" y="5" fill="#F49C52" />
<rect width="236" height="236" rx="47" ry="47" x="10" y="10" fill="url(#RSSg)" />
<circle cx="68" cy="189" r="24" fill="#FFF" />
<path d="M160 213h-34a82 82 0 0 0 -82 -82v-34a116 116 0 0 1 116 116z" fill="#FFF" />
<path d="M184 213A140 140 0 0 0 44 73 V 38a175 175 0 0 1 175 175z" fill="#FFF" />
</svg>
Web Feed Preview </h1>
<h2>
<xsl:value-of select="/rss/channel/title" />
</h2>
<p>
<xsl:value-of select="/rss/channel/description" />
</p>
<a class="head_link" target="_blank">
<xsl:attribute name="href">
<xsl:value-of select="/rss/channel/link" />
</xsl:attribute> Visit
Website &#x2192; </a>
</header>
<h2>Recent Items</h2>
<xsl:for-each select="/rss/channel/item">
<div class="pb-5">
<h3 class="mb-0">
<a target="_blank">
<xsl:attribute name="href">
<xsl:value-of select="link" />
</xsl:attribute>
<xsl:value-of select="title" />
</a>
</h3>
<small class="text-gray"> Published: <xsl:value-of select="pubDate" />
</small>
</div>
</xsl:for-each>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

BIN
public/fonts/ml.woff2 Normal file

Binary file not shown.

BIN
public/fonts/mlb.woff2 Normal file

Binary file not shown.

BIN
public/fonts/mlbi.woff2 Normal file

Binary file not shown.

BIN
public/fonts/mli.woff2 Normal file

Binary file not shown.

105
public/scripts/index.js Normal file
View file

@ -0,0 +1,105 @@
window.addEventListener("load", () => {
// menu keyboard controls
(() => {
const menuInput = document.getElementById("menu-toggle");
const menuButtonContainer = document.querySelector(
".menu-button-container"
);
const menuItems = document.querySelectorAll(".menu-primary li");
menuButtonContainer.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
menuInput.checked = !menuInput.checked;
}
});
menuItems.forEach((item) => {
item.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
item.querySelector("a").click();
}
});
});
document.addEventListener("keydown", (e) => {
if (e.key === "Escape" && menuInput.checked) menuInput.checked = false;
});
})();
// modal keyboard controls and scroll management
(() => {
const modalInputs = document.querySelectorAll(".modal-input");
if (!modalInputs) return;
const toggleBodyScroll = (disableScroll) => {
if (disableScroll) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "";
}
};
const checkModals = () => {
let isAnyModalOpen = false;
modalInputs.forEach((modalInput) => {
if (modalInput.checked) isAnyModalOpen = true;
});
toggleBodyScroll(isAnyModalOpen);
};
modalInputs.forEach((modalInput) => {
modalInput.addEventListener("change", checkModals);
});
document.addEventListener("keydown", (e) => {
if (e.key === "Escape") {
modalInputs.forEach((modalInput) => {
if (modalInput.checked) modalInput.checked = false;
});
toggleBodyScroll(false);
}
});
checkModals();
})();
// text toggle for media pages
(() => {
const button = document.querySelector("[data-toggle-button]");
const content = document.querySelector("[data-toggle-content]");
const text = document.querySelectorAll("[data-toggle-content] p");
const minHeight = 500; // this needs to match the height set on [data-toggle-content].text-toggle-hidden in text-toggle.css
const interiorHeight = Array.from(text).reduce(
(acc, node) => acc + node.scrollHeight,
0
);
if (!button || !content || !text) return;
if (interiorHeight < minHeight) {
content.classList.remove("text-toggle-hidden");
button.style.display = "none";
return;
}
button.addEventListener("click", () => {
const isHidden = content.classList.toggle("text-toggle-hidden");
button.textContent = isHidden ? "Show more" : "Show less";
});
})();
// pagination
(() => {
const dropdown = document.querySelector(".pagination select.client-side");
if (dropdown)
dropdown.addEventListener("change", (event) => {
const selectedOption = event.target.options[event.target.selectedIndex];
const selectedHref = selectedOption.getAttribute("data-href");
if (selectedHref) window.location.href = selectedHref;
});
})();
});