no semicolons
This commit is contained in:
parent
320b777b99
commit
bf93638810
7 changed files with 528 additions and 134 deletions
|
@ -1,65 +1,65 @@
|
||||||
const darkModeMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
||||||
|
|
||||||
/** @param {Event} evt */
|
/** @param {Event} evt */
|
||||||
function removeEffect({ target }) {
|
function removeEffect({ target }) {
|
||||||
const effectsLayer = document.querySelector("#effects");
|
const effectsLayer = document.querySelector("#effects")
|
||||||
if (effectsLayer == null) { return };
|
if (effectsLayer == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const effects = Array.from(effectsLayer.children).filter(
|
const effects = Array.from(effectsLayer.children).filter(
|
||||||
(e) => e['__effectParent'] === target,
|
(e) => e["__effectParent"] === target
|
||||||
);
|
)
|
||||||
effects.forEach((e) => {
|
effects.forEach((e) => {
|
||||||
e.getAnimations().forEach((anim) => {
|
e.getAnimations().forEach((anim) => {
|
||||||
if (+(anim.currentTime ?? 0) < 100) {
|
if (+(anim.currentTime ?? 0) < 100) {
|
||||||
anim.pause();
|
anim.pause()
|
||||||
effectsLayer.removeChild(e);
|
effectsLayer.removeChild(e)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
anim.pause();
|
anim.pause()
|
||||||
anim.updatePlaybackRate(-0.25);
|
anim.updatePlaybackRate(-0.25)
|
||||||
anim.play();
|
anim.play()
|
||||||
anim.addEventListener("finish", () => {
|
anim.addEventListener("finish", () => {
|
||||||
if (effectsLayer.contains(e)) {
|
if (effectsLayer.contains(e)) {
|
||||||
effectsLayer.removeChild(e);
|
effectsLayer.removeChild(e)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function isElement(target: EventTarget | null): target is Element {
|
function isElement(target: EventTarget | null): target is Element {
|
||||||
return target !== null && typeof target["matches"] === 'function'
|
return target !== null && typeof target["matches"] === "function"
|
||||||
}
|
}
|
||||||
|
|
||||||
function addEffect({ target }: UIEvent) {
|
function addEffect({ target }: UIEvent) {
|
||||||
const effectsLayer = document.querySelector("#effects");
|
const effectsLayer = document.querySelector("#effects")
|
||||||
if (
|
if (
|
||||||
!isElement(target) ||
|
!isElement(target) ||
|
||||||
!target.matches("a[href],.nav-toggle-button,button,input[type='radio']")
|
!target.matches("a[href],.nav-toggle-button,button,input[type='radio']")
|
||||||
) {
|
) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
const color = window.getComputedStyle(target).getPropertyValue('--glowColor');
|
const color = window.getComputedStyle(target).getPropertyValue("--glowColor")
|
||||||
const rects = Array.from(target.getClientRects());
|
const rects = Array.from(target.getClientRects())
|
||||||
Array.from(target.children).forEach((child) => {
|
Array.from(target.children).forEach((child) => {
|
||||||
rects.push(...Array.from(child.getClientRects()));
|
rects.push(...Array.from(child.getClientRects()))
|
||||||
});
|
})
|
||||||
rects.forEach((rect) => {
|
rects.forEach((rect) => {
|
||||||
const { top, left, width, height } = rect;
|
const { top, left, width, height } = rect
|
||||||
const newEffect = document.createElement("div");
|
const newEffect = document.createElement("div")
|
||||||
newEffect['__effectParent'] = target;
|
newEffect["__effectParent"] = target
|
||||||
newEffect.classList.add("effect-instance");
|
newEffect.classList.add("effect-instance")
|
||||||
const padding = "10rem";
|
const padding = "10rem"
|
||||||
newEffect.style.top = `calc(${top + window.scrollY}px - ${padding})`;
|
newEffect.style.top = `calc(${top + window.scrollY}px - ${padding})`
|
||||||
newEffect.style.left = `calc(${left + window.scrollX}px - ${padding})`;
|
newEffect.style.left = `calc(${left + window.scrollX}px - ${padding})`
|
||||||
newEffect.style.width = `calc(${width}px + 2 * ${padding})`;
|
newEffect.style.width = `calc(${width}px + 2 * ${padding})`
|
||||||
newEffect.style.height = `calc(${height}px + 2 * ${padding})`;
|
newEffect.style.height = `calc(${height}px + 2 * ${padding})`
|
||||||
newEffect.style.setProperty('--glowColor', color);
|
newEffect.style.setProperty("--glowColor", color)
|
||||||
effectsLayer?.appendChild(newEffect);
|
effectsLayer?.appendChild(newEffect)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("mouseenter", addEffect, true);
|
document.addEventListener("mouseenter", addEffect, true)
|
||||||
document.addEventListener("focus", addEffect, true);
|
document.addEventListener("focus", addEffect, true)
|
||||||
|
|
||||||
document.addEventListener("mouseleave", removeEffect, true);
|
document.addEventListener("mouseleave", removeEffect, true)
|
||||||
document.addEventListener("blur", removeEffect, true);
|
document.addEventListener("blur", removeEffect, true)
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
import fs from "fs";
|
import fs from "fs"
|
||||||
import path from "path";
|
import path from "path"
|
||||||
import md from "markdown-it";
|
import md from "markdown-it"
|
||||||
import mdAnchor from "markdown-it-anchor";
|
import mdAnchor from "markdown-it-anchor"
|
||||||
import { spoiler as mdSpoiler } from "@mdit/plugin-spoiler";
|
import { spoiler as mdSpoiler } from "@mdit/plugin-spoiler"
|
||||||
import { footnote as mdFootnote } from "@mdit/plugin-footnote";
|
import { footnote as mdFootnote } from "@mdit/plugin-footnote"
|
||||||
import mdLinkAttributes from "markdown-it-link-attributes";
|
import mdLinkAttributes from "markdown-it-link-attributes"
|
||||||
import mdPrism from "markdown-it-prism";
|
import mdPrism from "markdown-it-prism"
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs"
|
||||||
import utc from "dayjs/plugin/utc.js";
|
import utc from "dayjs/plugin/utc.js"
|
||||||
import clean from "eleventy-plugin-clean";
|
import site from "./site/_data/site.js"
|
||||||
import toc from "eleventy-plugin-toc";
|
import clean from "eleventy-plugin-clean"
|
||||||
import site from "./site/_data/site.js";
|
import toc from "eleventy-plugin-toc"
|
||||||
import { feedPlugin as EleventyFeedPlugin } from "@11ty/eleventy-plugin-rss";
|
import { feedPlugin as EleventyFeedPlugin } from "@11ty/eleventy-plugin-rss"
|
||||||
import EleventyVitePlugin from "@11ty/eleventy-plugin-vite";
|
import EleventyVitePlugin from "@11ty/eleventy-plugin-vite"
|
||||||
import { execSync } from "child_process";
|
import { ViteMinifyPlugin } from "vite-plugin-minify"
|
||||||
import fetch from "@11ty/eleventy-fetch";
|
import { execSync } from "child_process"
|
||||||
import { XMLValidator, XMLParser } from "fast-xml-parser";
|
import fetch from "@11ty/eleventy-fetch"
|
||||||
import { ViteMinifyPlugin } from "vite-plugin-minify";
|
import { XMLValidator, XMLParser } from "fast-xml-parser"
|
||||||
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc)
|
||||||
|
|
||||||
export default async (config) => {
|
export default async (config) => {
|
||||||
const slugify = config.getFilter("slugify");
|
const slugify = config.getFilter("slugify")
|
||||||
const url = config.getFilter("url");
|
const url = config.getFilter("url")
|
||||||
const mdLib = md({
|
const mdLib = md({
|
||||||
html: true,
|
html: true,
|
||||||
breaks: true,
|
breaks: true,
|
||||||
|
@ -42,94 +42,92 @@ export default async (config) => {
|
||||||
})
|
})
|
||||||
.use(mdLinkAttributes, {
|
.use(mdLinkAttributes, {
|
||||||
matcher(href) {
|
matcher(href) {
|
||||||
return href.match(/^https?:\/\//);
|
return href.match(/^https?:\/\//)
|
||||||
},
|
},
|
||||||
attrs: {
|
attrs: {
|
||||||
target: "_blank",
|
target: "_blank",
|
||||||
rel: "noopener",
|
rel: "noopener",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.use(mdPrism);
|
.use(mdPrism)
|
||||||
mdLib.renderer.rules.render_footnote_anchor = (
|
mdLib.renderer.rules.render_footnote_anchor = (
|
||||||
tokens,
|
tokens,
|
||||||
idx,
|
idx,
|
||||||
options,
|
options,
|
||||||
env,
|
env,
|
||||||
slf,
|
slf
|
||||||
) => {
|
) => {
|
||||||
let id = slf.rules.footnote_anchor_name(tokens, idx, options, env, slf);
|
let id = slf.rules.footnote_anchor_name(tokens, idx, options, env, slf)
|
||||||
if (tokens[idx].meta.subId > 0) id += `:${tokens[idx].meta.subId}`;
|
if (tokens[idx].meta.subId > 0) id += `:${tokens[idx].meta.subId}`
|
||||||
/* ↩ with escape code to prevent display as Apple Emoji on iOS */
|
/* ↩ with escape code to prevent display as Apple Emoji on iOS */
|
||||||
return ` <a href="#fnref${id}" class="footnote-backref">\u21a9\uFE0E</a>`;
|
return ` <a href="#fnref${id}" class="footnote-backref">\u21a9\uFE0E</a>`
|
||||||
};
|
}
|
||||||
config.setLibrary("md", mdLib);
|
config.setLibrary("md", mdLib)
|
||||||
config.addPassthroughCopy({
|
config.addPassthroughCopy({
|
||||||
assets: "/",
|
assets: "/",
|
||||||
});
|
})
|
||||||
|
|
||||||
// collection from music folder
|
// collection from music folder
|
||||||
config.addPassthroughCopy("site/music", {
|
config.addPassthroughCopy("site/music", {
|
||||||
rename: (filename) => {
|
rename: (filename) => {
|
||||||
const ext = path.extname(filename);
|
const ext = path.extname(filename)
|
||||||
const base = path.basename(filename, ext);
|
const base = path.basename(filename, ext)
|
||||||
return `${slugify(base)}${ext}`;
|
return `${slugify(base)}${ext}`
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
config.addCollection("music", () => {
|
config.addCollection("music", () => {
|
||||||
const musicFiles = fs.readdirSync("./site/music/").map((filename) => {
|
const musicFiles = fs.readdirSync("./site/music/").map((filename) => {
|
||||||
const ext = path.extname(filename);
|
const ext = path.extname(filename)
|
||||||
const base = path.basename(filename, ext);
|
const base = path.basename(filename, ext)
|
||||||
const absUrl = `/music/${slugify(base)}${ext}`;
|
const absUrl = `/music/${slugify(base)}${ext}`
|
||||||
return {
|
return {
|
||||||
data: {
|
data: {
|
||||||
title: base,
|
title: base,
|
||||||
tags: ["music"],
|
tags: ["music"],
|
||||||
},
|
},
|
||||||
url: url(absUrl),
|
url: url(absUrl),
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
return musicFiles;
|
return musicFiles
|
||||||
});
|
})
|
||||||
|
|
||||||
config.addCollection("categories", (collectionApi) => {
|
config.addCollection("categories", (collectionApi) => {
|
||||||
const posts = collectionApi
|
const posts = collectionApi
|
||||||
.getFilteredByTag("posts")
|
.getFilteredByTag("posts")
|
||||||
.filter(
|
.filter((p) => !p.data.draft || process.env.ELEVENTY_RUN_MODE !== "build")
|
||||||
(p) => !p.data.draft || process.env.ELEVENTY_RUN_MODE !== "build",
|
|
||||||
);
|
|
||||||
|
|
||||||
const categories = posts.reduce((tags, post) => {
|
const categories = posts.reduce((tags, post) => {
|
||||||
post.data.tags
|
post.data.tags
|
||||||
.filter((tag) => tag !== "posts")
|
.filter((tag) => tag !== "posts")
|
||||||
.forEach((tag) => {
|
.forEach((tag) => {
|
||||||
const prev = tags[tag] ?? { id: Object.keys(tags).length, count: 0 };
|
const prev = tags[tag] ?? { id: Object.keys(tags).length, count: 0 }
|
||||||
tags[tag] = { ...prev, count: prev.count + 1 };
|
tags[tag] = { ...prev, count: prev.count + 1 }
|
||||||
});
|
})
|
||||||
return tags;
|
return tags
|
||||||
}, {});
|
}, {})
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
Object.entries(categories).sort((a, b) => {
|
Object.entries(categories).sort((a, b) => {
|
||||||
return b[1].count - a[1].count;
|
return b[1].count - a[1].count
|
||||||
}),
|
})
|
||||||
);
|
)
|
||||||
});
|
})
|
||||||
|
|
||||||
config.addCollection("webroll", fetchShaarliWebroll);
|
config.addCollection("webroll", fetchShaarliWebroll)
|
||||||
|
|
||||||
config.addFilter("toISOString", (dateString) => {
|
config.addFilter("toISOString", (dateString) => {
|
||||||
return new Date(dateString).toISOString();
|
return new Date(dateString).toISOString()
|
||||||
});
|
})
|
||||||
config.addFilter("formatDate", (date, format) => {
|
config.addFilter("formatDate", (date, format) => {
|
||||||
return dayjs(date).utc().format(format);
|
return dayjs(date).utc().format(format)
|
||||||
});
|
})
|
||||||
config.addFilter("markdown", (markdownString) => {
|
config.addFilter("markdown", (markdownString) => {
|
||||||
return mdLib.renderInline(String(markdownString ?? "").trim());
|
return mdLib.renderInline(String(markdownString ?? "").trim())
|
||||||
});
|
})
|
||||||
|
|
||||||
clean.updateFileRecord("dist");
|
clean.updateFileRecord("dist")
|
||||||
|
|
||||||
const buildTime = new Date().toISOString().replace(/[:.-]/g, "");
|
const buildTime = new Date().toISOString().replace(/[:.-]/g, "")
|
||||||
config.addGlobalData("buildTime", buildTime);
|
config.addGlobalData("buildTime", buildTime)
|
||||||
|
|
||||||
config.addPlugin(EleventyFeedPlugin, {
|
config.addPlugin(EleventyFeedPlugin, {
|
||||||
type: "atom", // "atom", ""rss", or "json"
|
type: "atom", // "atom", ""rss", or "json"
|
||||||
|
@ -149,61 +147,65 @@ export default async (config) => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
stylesheet: "/simple-atom.xslt",
|
stylesheet: "/simple-atom.xslt",
|
||||||
});
|
})
|
||||||
|
|
||||||
config.addPlugin(EleventyVitePlugin, {
|
config.addPlugin(EleventyVitePlugin, {
|
||||||
viteOptions: {
|
viteOptions: {
|
||||||
server: {
|
server: {
|
||||||
port: 8080
|
port: 8080,
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
mode: 'production',
|
mode: "production",
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [ViteMinifyPlugin({})],
|
||||||
ViteMinifyPlugin({})
|
},
|
||||||
]
|
})
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
config.addPlugin(toc);
|
config.addPlugin(toc)
|
||||||
|
|
||||||
config.on("eleventy.after", () => {
|
config.on("eleventy.after", () => {
|
||||||
execSync(`npx pagefind --site dist --glob \"**/*.html\"`, {
|
execSync(`npx pagefind --site dist --glob \"**/*.html\"`, {
|
||||||
encoding: "utf-8",
|
encoding: "utf-8",
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dir: {
|
dir: {
|
||||||
input: "site",
|
input: "site",
|
||||||
output: "dist",
|
output: "dist",
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
async function fetchShaarliWebroll() {
|
async function fetchShaarliWebroll() {
|
||||||
const url = "https://links.apps.seigler.net/feed/atom?&searchtags=%24webroll";
|
const url = "https://links.apps.seigler.net/feed/atom?&searchtags=%24webroll"
|
||||||
const urlTextContent = await fetch(url, { duration: "30s", type: "text" });
|
const urlTextContent = await fetch(url, { duration: "30s", type: "text" })
|
||||||
const validation = XMLValidator.validate(urlTextContent);
|
const validation = XMLValidator.validate(urlTextContent)
|
||||||
let feedContent;
|
let feedContent
|
||||||
if (validation === true) {
|
if (validation === true) {
|
||||||
feedContent = new XMLParser({
|
feedContent = new XMLParser({
|
||||||
ignoreAttributes: false,
|
ignoreAttributes: false,
|
||||||
}).parse(urlTextContent).feed.entry;
|
}).parse(urlTextContent).feed.entry
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Invalid XML from webroll feed. Reason: ${validation.err.msg}`);
|
throw new Error(
|
||||||
|
`Invalid XML from webroll feed. Reason: ${validation.err.msg}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
const entries = feedContent.map(entry => {
|
const entries = feedContent.map((entry) => {
|
||||||
return {
|
return {
|
||||||
url: entry.link['@_href'],
|
url: entry.link["@_href"],
|
||||||
data: {
|
data: {
|
||||||
title: entry.title,
|
title: entry.title,
|
||||||
date: new Date(entry.published),
|
date: new Date(entry.published),
|
||||||
description: entry.content['#text'].split('\n<br>— <a href="https://links.apps.seigler.net/')[0],
|
description: entry.content["#text"].split(
|
||||||
tags: entry.category.map(category => category['@_label']).filter(category => category !== '$webroll'),
|
'\n<br>— <a href="https://links.apps.seigler.net/'
|
||||||
}
|
)[0],
|
||||||
|
tags: entry.category
|
||||||
|
.map((category) => category["@_label"])
|
||||||
|
.filter((category) => category !== "$webroll"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
return entries;
|
return entries
|
||||||
}
|
}
|
||||||
|
|
385
package-lock.json
generated
385
package-lock.json
generated
File diff suppressed because it is too large
Load diff
12
package.json
12
package.json
|
@ -12,7 +12,7 @@
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "Joshua Seigler",
|
"author": "Joshua Seigler",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"devDependencies": {
|
||||||
"@11ty/eleventy": "^3.1.0",
|
"@11ty/eleventy": "^3.1.0",
|
||||||
"@11ty/eleventy-fetch": "^5.1.0",
|
"@11ty/eleventy-fetch": "^5.1.0",
|
||||||
"@11ty/eleventy-plugin-rss": "^2.0.4",
|
"@11ty/eleventy-plugin-rss": "^2.0.4",
|
||||||
|
@ -31,5 +31,15 @@
|
||||||
"markdown-it-prism": "^3.0.0",
|
"markdown-it-prism": "^3.0.0",
|
||||||
"pagefind": "^1.3.0",
|
"pagefind": "^1.3.0",
|
||||||
"vite-plugin-minify": "^2.1.0"
|
"vite-plugin-minify": "^2.1.0"
|
||||||
|
},
|
||||||
|
"prettier": {
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "*.{js,ts}",
|
||||||
|
"options": {
|
||||||
|
"semi": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const isDev = process.env.ELEVENTY_ENV === "development";
|
const isDev = process.env.ELEVENTY_ENV === "development";
|
||||||
|
|
||||||
const baseUrl = isDev ? "localhost:8080" : "https://joshua.seigler.net/";
|
const baseUrl = isDev ? "http://localhost:8080" : "https://joshua.seigler.net";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "joshua.seigler.net",
|
title: "joshua.seigler.net",
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<a
|
<a
|
||||||
class="tag"
|
class="tag"
|
||||||
style="--tagIndex:{{ collections.categories[tag].id }}"
|
style="--tagIndex:{{ collections.categories[tag].id }}"
|
||||||
href="/tags/{{ tag | slugify }}"
|
href="/tags/{{ tag | slugify }}/"
|
||||||
>{{ tag }}</a>
|
>{{ tag }}</a>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{% endfor -%}
|
{% endfor -%}
|
||||||
|
|
|
@ -13,7 +13,6 @@ useTitle: true
|
||||||
{%- if item.data.date -%}
|
{%- if item.data.date -%}
|
||||||
<aside>{{item.data.date | formatDate("MMMM DD, YYYY") }}</aside>
|
<aside>{{item.data.date | formatDate("MMMM DD, YYYY") }}</aside>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
<aside>Tags: {{ item.data.tags | join(", ") }}</aside>
|
|
||||||
{{item.data.description | safe}}
|
{{item.data.description | safe}}
|
||||||
</li>
|
</li>
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue