diff --git a/package-lock.json b/package-lock.json
index dda9813..4ae9e5f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6,7 +6,7 @@
"": {
"dependencies": {
"@weborigami/dropbox": "0.0.10",
- "@weborigami/origami": "0.1.0",
+ "@weborigami/origami": "0.2.9",
"dotenv": "^16.4.5",
"html": "github:jimniels/html",
"html-minifier": "^4.0.0",
@@ -395,54 +395,55 @@
}
},
"node_modules/@weborigami/origami": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/@weborigami/origami/-/origami-0.1.0.tgz",
- "integrity": "sha512-qtQC1ZOIbQb9XGqNFG93nBUmbJBYeA2A5ExWlG8JYbPudAcmw6JHSKGwjb+oLWpMSckTrHIcxHi4SLJs73WFNg==",
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/@weborigami/origami/-/origami-0.2.9.tgz",
+ "integrity": "sha512-qaKayAV13fCn7Gle9+euTy7mtioTnmLeo59a6OFDSpAppzADJqPfGaGalJfnKznMY9svYRnI2RMCAdxKTF7+tw==",
"dependencies": {
- "@weborigami/async-tree": "0.1.0",
- "@weborigami/language": "0.1.0",
- "@weborigami/types": "0.1.0",
+ "@weborigami/async-tree": "0.2.9",
+ "@weborigami/language": "0.2.9",
+ "@weborigami/types": "0.2.9",
"exif-parser": "0.1.12",
"graphviz-wasm": "3.0.2",
- "highlight.js": "11.10.0",
- "marked": "14.1.2",
- "marked-gfm-heading-id": "4.1.0",
- "marked-highlight": "2.1.4",
- "marked-smartypants": "1.1.8",
+ "highlight.js": "11.11.1",
+ "marked": "15.0.7",
+ "marked-gfm-heading-id": "4.1.1",
+ "marked-highlight": "2.2.1",
+ "marked-smartypants": "1.1.9",
"sharp": "0.33.5",
- "yaml": "2.5.1"
+ "yaml": "2.7.0"
},
"bin": {
"ori": "src/cli/cli.js"
}
},
"node_modules/@weborigami/origami/node_modules/@weborigami/async-tree": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/@weborigami/async-tree/-/async-tree-0.1.0.tgz",
- "integrity": "sha512-r83v09iBfMMvCafVKOjGmVWGzthDxeL86YARqlSvDeUgUrID0uBJN6qiDUNrLSa6VlD96u9pbBd4C1t80fPBrA==",
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/@weborigami/async-tree/-/async-tree-0.2.9.tgz",
+ "integrity": "sha512-B3M5pEYXDVYm1Y2RhQlcjEGRHF+dFaKxS3VNLKq1icMTs6uIzrmnx2emDmxDGFK2ULVLJACn4fgytUfPnC9JWg==",
"dependencies": {
- "@weborigami/types": "0.1.0"
+ "@weborigami/types": "0.2.9"
}
},
"node_modules/@weborigami/origami/node_modules/@weborigami/language": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/@weborigami/language/-/language-0.1.0.tgz",
- "integrity": "sha512-okR9ev9nfDdly6oaEUYsBIrJwwkuoqxt/kypJSkp1Tmlj3T77qwmGFGkYRa8riBWygkeRVNzLmKgQ2CmiGGryA==",
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/@weborigami/language/-/language-0.2.9.tgz",
+ "integrity": "sha512-JxPu690a+9qEvSNwO6fpYZX+cg2ZYx6rlgC0Lopdn48ysA56IttaT01eDNeMuY79VRWJINZDM2Dl3Xi1EaIJ7Q==",
"dependencies": {
- "@weborigami/async-tree": "0.1.0",
- "@weborigami/types": "0.1.0",
+ "@weborigami/async-tree": "0.2.9",
+ "@weborigami/types": "0.2.9",
"watcher": "2.3.1"
}
},
"node_modules/@weborigami/origami/node_modules/@weborigami/types": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/@weborigami/types/-/types-0.1.0.tgz",
- "integrity": "sha512-InGRVeVOFiKI/qPEpW6FdyxMNgeOYeqXZHDW5/3NUazZJvMq+p8wB+OBY4t1ykIgArJlk/Zab0Hy8j9GwjVgXQ=="
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/@weborigami/types/-/types-0.2.9.tgz",
+ "integrity": "sha512-VdV8TvRJQ1BLj2385tesMfXtpF4pYeeuGBjfhNALK9vP8av6/a/j2mwbfIGx2v2dUZ1vXI/XdFv6Q0oEeEwo4g=="
},
"node_modules/@weborigami/origami/node_modules/marked": {
- "version": "14.1.2",
- "resolved": "https://registry.npmjs.org/marked/-/marked-14.1.2.tgz",
- "integrity": "sha512-f3r0yqpz31VXiDB/wj9GaOB0a2PRLQl6vJmXiFrniNwjkKdvakqJRULhjFKJpxOchlCRiG5fcacoUZY5Xa6PEQ==",
+ "version": "15.0.7",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.7.tgz",
+ "integrity": "sha512-dgLIeKGLx5FwziAnsk4ONoGwHwGPJzselimvlVskE9XLN4Orv9u2VA3GWw/lYUqjfA0rUT/6fqKwfZJapP9BEg==",
+ "license": "MIT",
"bin": {
"marked": "bin/marked.js"
},
@@ -451,14 +452,15 @@
}
},
"node_modules/@weborigami/origami/node_modules/marked-gfm-heading-id": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/marked-gfm-heading-id/-/marked-gfm-heading-id-4.1.0.tgz",
- "integrity": "sha512-xRvV65Fnpq1krNspnyGsBvP0Y6h7/FrJ6U6y4e6zCWffiC1KxFFxFUKVu8ufMHop2xdvpwyWj5jPeA5W5x/6Zw==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/marked-gfm-heading-id/-/marked-gfm-heading-id-4.1.1.tgz",
+ "integrity": "sha512-EeQZieAQmsI6c2tWLx0ETd0VjPwLV8qi+HT0dIsfVMERm0rCIuXfRvZXJbo1SgUi++lmuR1LVY+QzgNiLNvVpw==",
+ "license": "MIT",
"dependencies": {
"github-slugger": "^2.0.0"
},
"peerDependencies": {
- "marked": ">=13 <15"
+ "marked": ">=13 <16"
}
},
"node_modules/@weborigami/types": {
@@ -573,7 +575,8 @@
"node_modules/github-slugger": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz",
- "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="
+ "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==",
+ "license": "ISC"
},
"node_modules/graphviz-wasm": {
"version": "3.0.2",
@@ -593,9 +596,10 @@
}
},
"node_modules/highlight.js": {
- "version": "11.10.0",
- "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.10.0.tgz",
- "integrity": "sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==",
+ "version": "11.11.1",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
+ "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
+ "license": "BSD-3-Clause",
"engines": {
"node": ">=12.0.0"
}
@@ -646,22 +650,24 @@
}
},
"node_modules/marked-highlight": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/marked-highlight/-/marked-highlight-2.1.4.tgz",
- "integrity": "sha512-D1GOkcdzP+1dzjoColL7umojefFrASDuLeyaHS0Zr/Uo9jkr1V6vpLRCzfi1djmEaWyK0SYMFtHnpkZ+cwFT1w==",
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/marked-highlight/-/marked-highlight-2.2.1.tgz",
+ "integrity": "sha512-SiCIeEiQbs9TxGwle9/OwbOejHCZsohQRaNTY2u8euEXYt2rYUFoiImUirThU3Gd/o6Q1gHGtH9qloHlbJpNIA==",
+ "license": "MIT",
"peerDependencies": {
- "marked": ">=4 <15"
+ "marked": ">=4 <16"
}
},
"node_modules/marked-smartypants": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/marked-smartypants/-/marked-smartypants-1.1.8.tgz",
- "integrity": "sha512-2n8oSjL2gSkH6M0dSdRIyLgqqky03iKQkdmoaylmIzwIhYTW204S7ry6zP2iqwSl0zSlJH2xmWgxlZ/4XB1CdQ==",
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/marked-smartypants/-/marked-smartypants-1.1.9.tgz",
+ "integrity": "sha512-VPeuaUr5IWptI7nJdgQ9ugrLWYGv13NdzEXTtKY3cmB4aRWOI2RzhLlf+xQp6Wnob9SAPO2sNVlfSJr+nflk/A==",
+ "license": "MIT",
"dependencies": {
"smartypants": "^0.2.2"
},
"peerDependencies": {
- "marked": ">=4 <15"
+ "marked": ">=4 <16"
}
},
"node_modules/no-case": {
@@ -786,6 +792,7 @@
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/smartypants/-/smartypants-0.2.2.tgz",
"integrity": "sha512-TzobUYoEft/xBtb2voRPryAUIvYguG0V7Tt3de79I1WfXgCwelqVsGuZSnu3GFGRZhXR90AeEYIM+icuB/S06Q==",
+ "license": "BSD-3-Clause",
"bin": {
"smartypants": "bin/smartypants.js",
"smartypantsu": "bin/smartypantsu.js"
@@ -861,9 +868,10 @@
}
},
"node_modules/yaml": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz",
- "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==",
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz",
+ "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==",
+ "license": "ISC",
"bin": {
"yaml": "bin.mjs"
},
diff --git a/package.json b/package.json
index 15f3ede..f2b6152 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
},
"dependencies": {
"@weborigami/dropbox": "0.0.10",
- "@weborigami/origami": "0.1.0",
+ "@weborigami/origami": "0.2.9",
"dotenv": "^16.4.5",
"html": "github:jimniels/html",
"html-minifier": "^4.0.0",
diff --git a/src/components/Document.js b/src/components/Document.js
new file mode 100644
index 0000000..2ec97c0
--- /dev/null
+++ b/src/components/Document.js
@@ -0,0 +1,192 @@
+import fs from "fs";
+import html from "html";
+import path from "node:path";
+import { fileURLToPath } from "node:url";
+import { themes } from "../routes/Theme.js";
+
+const srcPath = path.dirname(fileURLToPath(import.meta.url));
+
+const normalize = fs
+ .readFileSync(
+ path.resolve(srcPath, "../../node_modules/normalize.css/normalize.css")
+ )
+ .toString();
+
+const importSvg = (file, label) =>
+ fs.readFileSync(path.resolve(srcPath, "../svgs", file)).toString();
+
+/**
+ *
+ * @param {{
+ * site: import("../../types").Site,
+ * children: string,
+ * breadcrumbs?: Array<{label: string, href: string}>,
+ * }} args
+ * @returns
+ */
+export default function Document({ site, children, breadcrumbs }) {
+ const { items } = site;
+ const activeThemeName = Object.keys(themes)[0];
+ const randomNoteId =
+ site.items[Math.floor(Math.random() * site.items.length)].id;
+
+ return html`
+
+
+
+ Jim Nielsen’s Notes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${children}
+
+
+
+
+ `;
+}
+
+function Theme({ activeThemeName }) {
+ return html`
+
+ `;
+}
diff --git a/src/index.dev.js b/src/index.dev.js
deleted file mode 100644
index 0c9dcc0..0000000
--- a/src/index.dev.js
+++ /dev/null
@@ -1,10 +0,0 @@
-export default async function Index(data) {
- // Path to the module with a timestamp to force reload
- const modulePath = `./index.js?t=${Date.now()}`;
-
- // Dynamically import the module
- const module = await import(modulePath);
- const result = module.default(data);
-
- return result;
-}
diff --git a/src/index.js b/src/index.js
index 02131be..1c2b440 100644
--- a/src/index.js
+++ b/src/index.js
@@ -2,6 +2,8 @@ import fs from "fs";
import html from "html";
import path from "node:path";
import { fileURLToPath } from "node:url";
+import { themes } from "./routes/Theme.js";
+import Document from "./components/Document.js";
const srcPath = path.dirname(fileURLToPath(import.meta.url));
@@ -14,416 +16,67 @@ const normalize = fs
const importSvg = (file, label) =>
fs.readFileSync(path.resolve(srcPath, "svgs", file)).toString();
-const themes = {
- Yamble: {
- bg: "#00171f",
- text: "#ffffff",
- "text-secondary": "#007ea7",
- highlight: "#00a8e8",
- },
- Halloween: {
- bg: "#252422",
- text: "#fffcf2",
- "text-secondary": "#ccc5b9",
- highlight: "#eb5e28",
- },
- Cadete: {
- bg: "#2b2d42",
- text: "#edf2f4",
- "text-secondary": "#8d99ae",
- highlight: "#ef233c",
- },
- Zizzoo: {
- bg: "#000000",
- text: "#ffffff",
- "text-secondary": "#999",
- highlight: "#fca311",
- },
- Valentines: {
- bg: "#590d22",
- text: "#fff0f3",
- "text-secondary": "#ff8fa3",
- highlight: "#ff4d6d",
- },
- WWW: {
- bg: "#fff",
- text: "#000",
- "text-secondary": "#999",
- highlight: "#0000EE",
- },
- Woods: {
- bg: "#fefae0",
- text: "#283618",
- "text-secondary": "#606c38",
- highlight: "#bc6c25",
- },
- Jazz: {
- bg: "#03071e",
- text: "#fff9e8",
- "text-secondary": "#f48c06",
- highlight: "#dc2f02",
- },
-};
-
-export default function template(items) {
+export default function template(items, args = {}) {
const activeThemeName = Object.keys(themes)[0];
- return html`
-
-
-
- Jim Nielsen’s Notes
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ${
- /*
- */ ""
- }
-
- ${items
- .map(
- ({
- id,
- content_html,
- content_text,
- date_published,
- title,
- url,
- external_url,
- _external_url_domain,
- tags,
- }) => html`
-
-
- ${content_html}
-
-
- `
- )
- .join("")}
-
-
+
+ ${content_html}
+
+
+ `
+ )
+ .join("")}
+ `;
-
-
- `;
+ return Document({
+ site: { items },
+ children,
+ breadcrumbs,
+ });
}
function Search(items) {
diff --git a/src/postsByDomain.js b/src/postsByDomain.js
new file mode 100644
index 0000000..3b05e22
--- /dev/null
+++ b/src/postsByDomain.js
@@ -0,0 +1,13 @@
+export default function postsByDomain(posts) {
+ const out = posts.reduce((acc, item) => {
+ if (acc[item._external_url_domain]) {
+ acc[item._external_url_domain].push(item);
+ // acc[item._external_url_domain]++;
+ } else {
+ acc[item._external_url_domain] = [item];
+ // acc[item._external_url_domain] = 1;
+ }
+ return acc;
+ }, {});
+ return out;
+}
diff --git a/src/routes/Theme.js b/src/routes/Theme.js
new file mode 100644
index 0000000..090062f
--- /dev/null
+++ b/src/routes/Theme.js
@@ -0,0 +1,179 @@
+export const themes = {
+ Yamble: {
+ bg: "#00171f",
+ text: "#ffffff",
+ "text-secondary": "#007ea7",
+ highlight: "#00a8e8",
+ },
+ Halloween: {
+ bg: "#252422",
+ text: "#fffcf2",
+ "text-secondary": "#ccc5b9",
+ highlight: "#eb5e28",
+ },
+ Cadete: {
+ bg: "#2b2d42",
+ text: "#edf2f4",
+ "text-secondary": "#8d99ae",
+ highlight: "#ef233c",
+ },
+ Zizzoo: {
+ bg: "#000000",
+ text: "#ffffff",
+ "text-secondary": "#999",
+ highlight: "#fca311",
+ },
+ Valentines: {
+ bg: "#590d22",
+ text: "#fff0f3",
+ "text-secondary": "#ff8fa3",
+ highlight: "#ff4d6d",
+ },
+ WWW: {
+ bg: "#fff",
+ text: "#000",
+ "text-secondary": "#999",
+ highlight: "#0000EE",
+ },
+ Woods: {
+ bg: "#fefae0",
+ text: "#283618",
+ "text-secondary": "#606c38",
+ highlight: "#bc6c25",
+ },
+ Jazz: {
+ bg: "#03071e",
+ text: "#fff9e8",
+ "text-secondary": "#f48c06",
+ highlight: "#dc2f02",
+ },
+};
+
+function Theme({ activeThemeName }) {
+ return html`
+
+
+
+
+ `;
+}
diff --git a/src/site.ori b/src/site.ori
index 6b58a64..3650d75 100644
--- a/src/site.ori
+++ b/src/site.ori
@@ -1,10 +1,186 @@
{
- _postData = postData.ori/
- _truncatedPostData = { ..._postData, items: tree:take(_postData/items, 20) }
- public = {
- index.html = index.dev.js(_postData/items)
- favicon.ico
- feed.json = origami:json(_truncatedPostData)
- feed.xml = site:rss(_truncatedPostData)
+ (postData) = postData.ori/
+ (truncatedPostData) = { ...postData, items: tree:take(postData/items, 20) }
+
+ ...static/
+ index.html = index.js(postData/items)
+ favicon.ico
+ feed.json = origami:json(truncatedPostData)
+ feed.xml = site:rss(truncatedPostData)
+
+ id = tree:map(postData/items, {
+ key: (post) => post/id
+ value: (post) => {
+ index.html: index.js([post], {
+ breadcrumbs: [
+ {
+ label: post/id,
+ href: "/id/" + post/id
+ }
+ ]
+ })
+ }
+ })
+
+ // /by/tag|link|year
+ by = {
+ index.html = components/Document.js({
+ site: { items: postData/items },
+ breadcrumbs: [
+ {
+ label: "By",
+ href: "/by/"
+ }
+ ],
+ children:`By
+ `})
+
+ (postsByYear) = tree:reverse(tree:sort(tree:group(postData/items, (post) => post/date_published/slice(0, 4))))
+ year = {
+ index.html: components/Document.js({
+ site: { items: postData/items },
+ breadcrumbs: [
+ {
+ label: "By",
+ href: "/by/"
+ },
+ {
+ label: "Year",
+ href: "/by/year/"
+ }
+ ],
+ children: `
+
+ `
+ }),
+
+ ...tree:map(postsByYear, {
+ key: (posts, year) => year
+ value: (posts, year) => {
+ index.html: index.js(posts, {
+ breadcrumbs: [
+ {
+ label: "By",
+ href: "/by/"
+ },
+ {
+ label: "Year",
+ href: "/by/year/"
+ },
+ {
+ label: `${origami:slash/remove(year)} (${posts/length})`,
+ href: "/by/year/" + year
+ }
+ ]
+ })
+ }
+ })
+ }
+
+ link = {
+ index.html: components/Document.js({
+ site: { items: postData/items },
+ breadcrumbs: [
+ {
+ label: "By",
+ href: "/by/"
+ },
+ {
+ label: "Link",
+ href: "/by/link/"
+ }
+ ],
+ children: `
+
+
+
+ | Link |
+ Count |
+
+
+
+ ${tree:map(
+ tree:reverse(tree:sort(postsByDomain.js(postData/items), (posts) => posts/length),)
+ (posts, domain) => `
+ | ${domain} |
+ ${posts/length} |
+
`,
+ )}
+
+
+ `}),
+ ...tree:map(postsByDomain.js(postData/items), {
+ key: (posts, domain) => domain,
+ value: (posts, domain) => {
+ index.html: index.js(posts, {
+ breadcrumbs: [
+ {
+ label: "By",
+ href: "/by/"
+ },
+ {
+ label: "Link",
+ href: "/by/link/"
+ },
+ {
+ label: `${origami:slash/remove(domain)} (${posts/length})`,
+ href: "/by/link/" + domain
+ }
+ ]
+ })
+ }
+ })
+ }
+
+ (postsByTag) = tree:reverse(tree:sort(tree:group(postData/items, (post) => post/tags), (tags) => tags/length))
+ tag = {
+ index.html: components/Document.js({
+ site: { items: postData/items },
+ breadcrumbs: [
+ {
+ label: "By",
+ href: "/by/"
+ },
+ {
+ label: "Tag",
+ href: "/by/tag/"
+ },
+ ],
+ children: `
+
+ ${tree:map(postsByTag, (posts, tag) => `- ${tag} (${posts/length})
`)}
+
+ `
+ })
+ ...tree:map(postsByTag, {
+ key: (posts, tag) => tag,
+ value: (posts, tag) => {
+ index.html: index.js(posts, {
+ breadcrumbs: [
+ {
+ label: "By",
+ href: "/by/"
+ },
+ {
+ label: "Tag",
+ href: "/by/tag/"
+ },
+ {
+ label: `${origami:slash/remove(tag)} (${posts/length})`,
+ href: "/by/tag/" + tag
+ }
+ ]
+ })
+ }
+ })
+ }
}
-}/public
+}
diff --git a/src/static/styles.css b/src/static/styles.css
new file mode 100644
index 0000000..4388ccb
--- /dev/null
+++ b/src/static/styles.css
@@ -0,0 +1,269 @@
+html {
+ background: var(--c-bg);
+ scroll-behavior: smooth;
+}
+
+html,
+body {
+ font-size: 1em;
+}
+body {
+ font-family: Verdana, "Helvetica Neue", sans-serif;
+ line-height: 1.5;
+ width: 90%;
+ margin: 0 auto;
+ color: var(--c-text);
+}
+
+main {
+ max-width: 30rem;
+}
+
+a {
+ color: inherit;
+ text-decoration: none;
+ border-bottom: 1px solid;
+ transition: 0.1s ease color;
+}
+a:hover {
+ color: var(--c-highlight);
+ border-bottom: 2px solid var(--c-highlight);
+}
+
+pre {
+ overflow: scroll;
+}
+
+body > header {
+ margin: 4rem 0 0;
+ position: relative;
+}
+body > header h1 {
+ line-height: 1;
+ text-indent: 11rem;
+}
+body > header h1:after {
+ content: "";
+ width: 0.25rem;
+ height: 1.25em;
+ background-color: var(--c-highlight);
+ display: inline-block;
+ position: relative;
+ top: 0.2em;
+ animation: 1s blink step-end infinite;
+}
+body > header > :is(h1, p) {
+ margin: 0.5rem 0;
+ position: relative;
+ z-index: 1;
+}
+body > header p {
+ color: var(--c-text-secondary);
+ padding-left: 3rem;
+ line-height: 1.3;
+}
+body > header svg {
+ position: absolute;
+ left: -20px;
+ top: -30px;
+ fill: var(--c-highlight);
+ z-index: 0;
+}
+@keyframes blink {
+ from,
+ to {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0;
+ }
+}
+
+article > header > p {
+ color: var(--c-text-secondary);
+ font-size: 0.8125rem;
+ text-transform: uppercase;
+ letter-spacing: 0.05rem;
+ margin-bottom: 0;
+}
+article > footer {
+ color: var(--c-text-secondary);
+ font-size: 0.8125rem;
+ margin-bottom: 0;
+}
+article > footer > ul {
+ display: flex;
+ list-style-type: none;
+ gap: 0.5rem;
+ padding: 0;
+}
+article > footer > ul li:not(:last-child):after {
+ content: " · ";
+ padding: 0 0.25rem;
+}
+
+article header {
+ display: flex;
+ flex-direction: column;
+}
+article header h1 {
+ order: 2;
+}
+article h1 {
+ text-transform: uppercase;
+ letter-spacing: 0.0125rem;
+}
+article :is(h1, h2, h3, h4) {
+ font-size: 1rem;
+}
+blockquote {
+ border-left: 1px solid var(--c-highlight);
+ margin: 0;
+ padding-left: calc(1.618rem / 2);
+}
+article {
+ margin-bottom: 5rem;
+ padding-top: 5rem; /* enough space when anchoring to a thing */
+ overflow-wrap: break-word;
+}
+
+body > footer {
+ margin: calc(100vh - 6rem) 0 2rem;
+ color: var(--c-text-secondary);
+ font-size: 0.75rem;
+}
+
+.highlight {
+ color: var(--c-highlight);
+}
+
+nav {
+ display: flex;
+ flex-direction: column;
+ gap: 2rem;
+ z-index: 10;
+ background: var(--c-bg);
+ padding: 1rem 0 0.25rem 0;
+}
+@media screen and (min-width: 800px) {
+ nav {
+ gap: 3rem;
+ flex-direction: row;
+ align-items: center;
+ }
+}
+nav:after {
+ content: "";
+ position: absolute;
+ width: 100%;
+ top: 100%;
+ left: 0;
+ height: 3rem;
+ background: linear-gradient(0deg, transparent, var(--c-bg));
+ pointer-events: none;
+}
+
+@media screen and (min-width: 600px) {
+ body {
+ margin: 0 0 0 2rem;
+ }
+}
+
+.nav-links {
+ display: flex;
+ flex-direction: row;
+ gap: 0.5rem;
+ margin: 0;
+ list-style-type: none;
+ padding: 0;
+}
+
+.nav-icon {
+ border: none !important;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 50%;
+ box-shadow: 0 0 0 10px var(--c-bg);
+ background: var(--c-bg);
+ transition: 0.3s ease transform;
+}
+.nav-icon:active {
+ transform: scale(0.9);
+}
+.nav-icon:first-child {
+ z-index: 100;
+}
+
+.nav-icon {
+ width: 2.75rem;
+ height: 2.75rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ color: var(--c-text-secondary);
+}
+.nav-icon svg {
+ transition: 0.3s ease fill;
+ /* fill: var(--c-bg); */
+}
+.nav-icon:hover svg {
+ fill: var(--c-highlight);
+}
+.domain a {
+ color: var(--c-highlight);
+ border: none;
+}
+.domain a:hover {
+ border-bottom: 1px solid var(--c-highlight);
+}
+
+.breadcrumbs {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ list-style-type: none;
+ padding: 0;
+ margin: 0;
+ color: var(--c-text-secondary);
+ order: 1;
+
+ /* font-weight: 700; */
+}
+.nav-links {
+ order: 2;
+}
+
+@media screen and (min-width: 800px) {
+ .breadcrumbs {
+ flex-direction: row;
+ flex-wrap: wrap;
+ }
+}
+
+.breadcrumbs li:last-child {
+ color: var(--c-text);
+ font-weight: bold;
+}
+.breadcrumbs li:not(:last-child):after {
+ content: " › ";
+ padding: 0 0.25rem;
+ font-weight: 400;
+ opacity: 0.5;
+}
+.breadcrumbs [aria-current="page"] {
+ border-bottom: none;
+ pointer-events: none;
+}
+
+li:has([aria-current="page"]) + li [href="/by/"] {
+ font-weight: 400;
+ opacity: 0.5;
+ text-decoration: none;
+ border-bottom: none;
+
+ &:hover {
+ opacity: 1;
+ }
+}
diff --git a/src/svgs/heroicon-about.svg b/src/svgs/heroicon-about.svg
new file mode 100644
index 0000000..e856f8a
--- /dev/null
+++ b/src/svgs/heroicon-about.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/svgs/heroicon-home.svg b/src/svgs/heroicon-home.svg
new file mode 100644
index 0000000..1332414
--- /dev/null
+++ b/src/svgs/heroicon-home.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/svgs/heroicon-shuffle.svg b/src/svgs/heroicon-shuffle.svg
new file mode 100644
index 0000000..a86b20e
--- /dev/null
+++ b/src/svgs/heroicon-shuffle.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/svgs/icon-about.svg b/src/svgs/icon-about.svg
new file mode 100644
index 0000000..96a1251
--- /dev/null
+++ b/src/svgs/icon-about.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/svgs/icon-home.svg b/src/svgs/icon-home.svg
new file mode 100644
index 0000000..34e30f9
--- /dev/null
+++ b/src/svgs/icon-home.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/svgs/icon-rss.svg b/src/svgs/icon-rss.svg
index 250334a..376cc32 100644
--- a/src/svgs/icon-rss.svg
+++ b/src/svgs/icon-rss.svg
@@ -1,3 +1 @@
-
+
\ No newline at end of file
diff --git a/src/svgs/icon-shuffle.svg b/src/svgs/icon-shuffle.svg
index fd6f0dd..e15b864 100644
--- a/src/svgs/icon-shuffle.svg
+++ b/src/svgs/icon-shuffle.svg
@@ -1,3 +1 @@
-
+
\ No newline at end of file
diff --git a/src/svgs/icon-theme.svg b/src/svgs/icon-theme.svg
index 1c375bd..088ae12 100644
--- a/src/svgs/icon-theme.svg
+++ b/src/svgs/icon-theme.svg
@@ -1,3 +1 @@
-
+
\ No newline at end of file
diff --git a/types.d.ts b/types.d.ts
index 1a65240..fc0d696 100644
--- a/types.d.ts
+++ b/types.d.ts
@@ -6,6 +6,16 @@ export type Site = {
home_page_url: string;
feed_url: string;
items: Array;
+
+ notesById: Record;
+ noteIds: Array;
+ tagsById: Record<
+ string,
+ {
+ postIds: Array;
+ }
+ >;
+ tagIds: Array;
};
export type Note = {