diff --git a/package-lock.json b/package-lock.json
index 18962ff..5957454 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -19,12 +19,19 @@
"@types/cookie": "^0.5.1",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
+ "dateformat": "^5.0.3",
"eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte": "^2.30.0",
+ "mdsvex": "^0.11.0",
"prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.10.1",
+ "reading-time": "^1.5.0",
+ "rehype-autolink-headings": "^7.0.0",
+ "rehype-external-links": "^3.0.0",
+ "rehype-slug": "^6.0.0",
"sass": "^1.69.4",
+ "striptags": "^3.2.0",
"svelte": "^4.0.5",
"svelte-check": "^3.4.3",
"tslib": "^2.4.1",
@@ -740,6 +747,15 @@
"integrity": "sha512-CS2rOaoQ/eAgAfcTfq6amKG7bsN+EMcgGY4FAFQdvSj2y1ixvOZTUA9mOtCai7E1SYu283XNw7urKK30nP3wkQ==",
"dev": true
},
+ "node_modules/@types/hast": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.2.tgz",
+ "integrity": "sha512-B5hZHgHsXvfCoO3xgNJvBnX7N8p86TqQeGKXcokW4XXi+qY4vxxPSFYofytvVmpFxzPv7oxDQzjg5Un5m2/xiw==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
"node_modules/@types/json-schema": {
"version": "7.0.14",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz",
@@ -767,6 +783,12 @@
"integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==",
"dev": true
},
+ "node_modules/@types/unist": {
+ "version": "2.0.9",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.9.tgz",
+ "integrity": "sha512-zC0iXxAv1C1ERURduJueYzkzZ2zaGyc+P2c95hgkikHPr3z8EdUZOlgEQ5X0DRmwDZn+hekycQnoeiiRVrmilQ==",
+ "dev": true
+ },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.8.0.tgz",
@@ -956,6 +978,12 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+ "dev": true
+ },
"node_modules/@vitest/expect": {
"version": "0.32.4",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.32.4.tgz",
@@ -1176,6 +1204,16 @@
"dequal": "^2.0.3"
}
},
+ "node_modules/bail": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
+ "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -1410,6 +1448,15 @@
"node": ">=4"
}
},
+ "node_modules/dateformat": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-5.0.3.tgz",
+ "integrity": "sha512-Kvr6HmPXUMerlLcLF+Pwq3K7apHpYmGDVqrxcDasBg86UcKeTSNWbEzU8bwdXnxnR44FtMhJAxI4Bov6Y/KUfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.20"
+ }
+ },
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -1478,6 +1525,19 @@
"integrity": "sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==",
"dev": true
},
+ "node_modules/devlop": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+ "dev": true,
+ "dependencies": {
+ "dequal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/diff-sequences": {
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
@@ -1768,6 +1828,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -1912,6 +1978,12 @@
"node": "*"
}
},
+ "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==",
+ "dev": true
+ },
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -2012,6 +2084,45 @@
"node": ">=8"
}
},
+ "node_modules/hast-util-heading-rank": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-heading-rank/-/hast-util-heading-rank-3.0.0.tgz",
+ "integrity": "sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==",
+ "dev": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-is-element": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz",
+ "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==",
+ "dev": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-string": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.0.tgz",
+ "integrity": "sha512-OGkAxX1Ua3cbcW6EJ5pT/tslVb90uViVkcJ4ZZIMW/R33DX/AkcJcRrPebPwJkHYwlDHXz4aIwvAAaAdtrACFA==",
+ "dev": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/ignore": {
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@@ -2068,6 +2179,18 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
+ "node_modules/is-absolute-url": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-4.0.1.tgz",
+ "integrity": "sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -2119,6 +2242,18 @@
"node": ">=8"
}
},
+ "node_modules/is-plain-obj": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/is-reference": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz",
@@ -2294,6 +2429,21 @@
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
"dev": true
},
+ "node_modules/mdsvex": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/mdsvex/-/mdsvex-0.11.0.tgz",
+ "integrity": "sha512-gJF1s0N2nCmdxcKn8HDn0LKrN8poStqAicp6bBcsKFd/zkUBGLP5e7vnxu+g0pjBbDFOscUyI1mtHz+YK2TCDw==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^2.0.3",
+ "prism-svelte": "^0.4.7",
+ "prismjs": "^1.17.1",
+ "vfile-message": "^2.0.4"
+ },
+ "peerDependencies": {
+ "svelte": ">=3 <5"
+ }
+ },
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -2792,6 +2942,21 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
+ "node_modules/prism-svelte": {
+ "version": "0.4.7",
+ "resolved": "https://registry.npmjs.org/prism-svelte/-/prism-svelte-0.4.7.tgz",
+ "integrity": "sha512-yABh19CYbM24V7aS7TuPYRNMqthxwbvx6FF/Rw920YbyBWO3tnyPIqRMgHuSVsLmuHkkBS1Akyof463FVdkeDQ==",
+ "dev": true
+ },
+ "node_modules/prismjs": {
+ "version": "1.29.0",
+ "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
+ "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/punycode": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
@@ -2839,6 +3004,65 @@
"node": ">=8.10.0"
}
},
+ "node_modules/reading-time": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz",
+ "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==",
+ "dev": true
+ },
+ "node_modules/rehype-autolink-headings": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/rehype-autolink-headings/-/rehype-autolink-headings-7.0.0.tgz",
+ "integrity": "sha512-DLskejGYHQP9v7vUW85BeYIclgfMQ1IwAMx+0lm8Sr3cME2NThgy2OdTfosmuBA68fqP5o4FK+dknpUMFvlxYQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "hast-util-heading-rank": "^3.0.0",
+ "hast-util-is-element": "^3.0.0",
+ "unified": "^11.0.0",
+ "unist-util-visit": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-external-links": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/rehype-external-links/-/rehype-external-links-3.0.0.tgz",
+ "integrity": "sha512-yp+e5N9V3C6bwBeAC4n796kc86M4gJCdlVhiMTxIrJG5UHDMh+PJANf9heqORJbt1nrCbDwIlAZKjANIaVBbvw==",
+ "dev": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "hast-util-is-element": "^3.0.0",
+ "is-absolute-url": "^4.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "unist-util-visit": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-slug": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/rehype-slug/-/rehype-slug-6.0.0.tgz",
+ "integrity": "sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==",
+ "dev": true,
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "github-slugger": "^2.0.0",
+ "hast-util-heading-rank": "^3.0.0",
+ "hast-util-to-string": "^3.0.0",
+ "unist-util-visit": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@@ -3060,6 +3284,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/space-separated-tokens": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
+ "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/stackback": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
@@ -3120,6 +3354,12 @@
"url": "https://github.com/sponsors/antfu"
}
},
+ "node_modules/striptags": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/striptags/-/striptags-3.2.0.tgz",
+ "integrity": "sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw==",
+ "dev": true
+ },
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -3352,6 +3592,16 @@
"node": ">=6"
}
},
+ "node_modules/trough": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz",
+ "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==",
+ "dev": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/ts-api-utils": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
@@ -3440,6 +3690,104 @@
"integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==",
"dev": true
},
+ "node_modules/unified": {
+ "version": "11.0.3",
+ "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.3.tgz",
+ "integrity": "sha512-jlCV402P+YDcFcB2VcN/n8JasOddqIiaxv118wNBoZXEhOn+lYG7BR4Bfg2BwxvlK58dwbuH2w7GX2esAjL6Mg==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "bail": "^2.0.0",
+ "devlop": "^1.0.0",
+ "extend": "^3.0.0",
+ "is-plain-obj": "^4.0.0",
+ "trough": "^2.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unified/node_modules/@types/unist": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.1.tgz",
+ "integrity": "sha512-ue/hDUpPjC85m+PM9OQDMZr3LywT+CT6mPsQq8OJtCLiERkGRcQUFvu9XASF5XWqyZFXbf15lvb3JFJ4dRLWPg==",
+ "dev": true
+ },
+ "node_modules/unist-util-is": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
+ "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-is/node_modules/@types/unist": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.1.tgz",
+ "integrity": "sha512-ue/hDUpPjC85m+PM9OQDMZr3LywT+CT6mPsQq8OJtCLiERkGRcQUFvu9XASF5XWqyZFXbf15lvb3JFJ4dRLWPg==",
+ "dev": true
+ },
+ "node_modules/unist-util-stringify-position": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz",
+ "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^2.0.2"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
+ "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-parents": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
+ "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-parents/node_modules/@types/unist": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.1.tgz",
+ "integrity": "sha512-ue/hDUpPjC85m+PM9OQDMZr3LywT+CT6mPsQq8OJtCLiERkGRcQUFvu9XASF5XWqyZFXbf15lvb3JFJ4dRLWPg==",
+ "dev": true
+ },
+ "node_modules/unist-util-visit/node_modules/@types/unist": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.1.tgz",
+ "integrity": "sha512-ue/hDUpPjC85m+PM9OQDMZr3LywT+CT6mPsQq8OJtCLiERkGRcQUFvu9XASF5XWqyZFXbf15lvb3JFJ4dRLWPg==",
+ "dev": true
+ },
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -3455,6 +3803,68 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true
},
+ "node_modules/vfile": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz",
+ "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-stringify-position": "^4.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-message": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz",
+ "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "unist-util-stringify-position": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile/node_modules/@types/unist": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.1.tgz",
+ "integrity": "sha512-ue/hDUpPjC85m+PM9OQDMZr3LywT+CT6mPsQq8OJtCLiERkGRcQUFvu9XASF5XWqyZFXbf15lvb3JFJ4dRLWPg==",
+ "dev": true
+ },
+ "node_modules/vfile/node_modules/unist-util-stringify-position": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
+ "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile/node_modules/vfile-message": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz",
+ "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==",
+ "dev": true,
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
"node_modules/vite": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz",
diff --git a/package.json b/package.json
index b74030e..3effa80 100644
--- a/package.json
+++ b/package.json
@@ -18,12 +18,19 @@
"@types/cookie": "^0.5.1",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
+ "dateformat": "^5.0.3",
"eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte": "^2.30.0",
+ "mdsvex": "^0.11.0",
"prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.10.1",
+ "reading-time": "^1.5.0",
+ "rehype-autolink-headings": "^7.0.0",
+ "rehype-external-links": "^3.0.0",
+ "rehype-slug": "^6.0.0",
"sass": "^1.69.4",
+ "striptags": "^3.2.0",
"svelte": "^4.0.5",
"svelte-check": "^3.4.3",
"tslib": "^2.4.1",
diff --git a/src/lib/components/atoms/Button.story.svelte b/src/lib/components/atoms/Button.story.svelte
new file mode 100644
index 0000000..f672527
--- /dev/null
+++ b/src/lib/components/atoms/Button.story.svelte
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {text}
+
+
+
+
+
+ {text}
+
+
+
+
diff --git a/src/lib/components/atoms/Button.svelte b/src/lib/components/atoms/Button.svelte
new file mode 100644
index 0000000..f0bb660
--- /dev/null
+++ b/src/lib/components/atoms/Button.svelte
@@ -0,0 +1,132 @@
+
+
+
+ {#if $$slots['icon']}
+
+
+
+ {/if}
+
+
+
+
diff --git a/src/lib/components/atoms/Card.story.svelte b/src/lib/components/atoms/Card.story.svelte
new file mode 100644
index 0000000..18f5fa3
--- /dev/null
+++ b/src/lib/components/atoms/Card.story.svelte
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+ Card Content
+ Footer
+
+
+
+
+
+
+
+ Cards with images will automatically adapt between showing the image on top or bottom
+ depending on how wide it is.
+
+
+
+
+
+
+
+ Card Content
+ Footer
+
+
+
+
diff --git a/src/lib/components/atoms/Card.svelte b/src/lib/components/atoms/Card.svelte
new file mode 100644
index 0000000..ce347af
--- /dev/null
+++ b/src/lib/components/atoms/Card.svelte
@@ -0,0 +1,99 @@
+
+
+
+ {#if $$slots.image}
+
+
+
+ {/if}
+
+
+
+
+ {#if $$slots.footer}
+
+ {/if}
+
+
+
+
diff --git a/src/lib/components/atoms/Image.svelte b/src/lib/components/atoms/Image.svelte
new file mode 100644
index 0000000..b1fed68
--- /dev/null
+++ b/src/lib/components/atoms/Image.svelte
@@ -0,0 +1,48 @@
+
+
+
+
+
diff --git a/src/lib/components/atoms/Logo.story.svelte b/src/lib/components/atoms/Logo.story.svelte
new file mode 100644
index 0000000..c173713
--- /dev/null
+++ b/src/lib/components/atoms/Logo.story.svelte
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/components/atoms/Logo.svelte b/src/lib/components/atoms/Logo.svelte
new file mode 100644
index 0000000..10c35f1
--- /dev/null
+++ b/src/lib/components/atoms/Logo.svelte
@@ -0,0 +1,86 @@
+
+
+
+
+ Site Logo
+
+
+
+
diff --git a/src/lib/components/atoms/RssLink.svelte b/src/lib/components/atoms/RssLink.svelte
new file mode 100644
index 0000000..baf6416
--- /dev/null
+++ b/src/lib/components/atoms/RssLink.svelte
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/lib/components/atoms/SingleSparkle.svelte b/src/lib/components/atoms/SingleSparkle.svelte
new file mode 100644
index 0000000..6f1fe2f
--- /dev/null
+++ b/src/lib/components/atoms/SingleSparkle.svelte
@@ -0,0 +1,55 @@
+
+
+
+
+
diff --git a/src/lib/components/atoms/Sparkles.story.svelte b/src/lib/components/atoms/Sparkles.story.svelte
new file mode 100644
index 0000000..eb32165
--- /dev/null
+++ b/src/lib/components/atoms/Sparkles.story.svelte
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+ Sparkles can be added anywhere
+
+
+
+
diff --git a/src/lib/components/atoms/Sparkles.svelte b/src/lib/components/atoms/Sparkles.svelte
new file mode 100644
index 0000000..3ada195
--- /dev/null
+++ b/src/lib/components/atoms/Sparkles.svelte
@@ -0,0 +1,71 @@
+
+
+
+ {#each sparkles as sparkle (sparkle.id)}
+
+ {/each}
+
+
+
+
+
+
diff --git a/src/lib/components/atoms/Tag.story.svelte b/src/lib/components/atoms/Tag.story.svelte
new file mode 100644
index 0000000..e617d32
--- /dev/null
+++ b/src/lib/components/atoms/Tag.story.svelte
@@ -0,0 +1,17 @@
+
+
+
+
+ This is a Tag
+
+
+
+ This is a Tag
+
+
diff --git a/src/lib/components/atoms/Tag.svelte b/src/lib/components/atoms/Tag.svelte
new file mode 100644
index 0000000..024b9cf
--- /dev/null
+++ b/src/lib/components/atoms/Tag.svelte
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
diff --git a/src/lib/components/molecules/BlogPostCard.story.svelte b/src/lib/components/molecules/BlogPostCard.story.svelte
new file mode 100644
index 0000000..7b21f89
--- /dev/null
+++ b/src/lib/components/molecules/BlogPostCard.story.svelte
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/components/molecules/BlogPostCard.svelte b/src/lib/components/molecules/BlogPostCard.svelte
new file mode 100644
index 0000000..e79d505
--- /dev/null
+++ b/src/lib/components/molecules/BlogPostCard.svelte
@@ -0,0 +1,97 @@
+
+
+
+
+ {#if coverImage}
+
+ {/if}
+
+
+
+ {title}
+
+ {#if readingTime}
+
{readingTime}
+ {/if}
+ {#if excerpt}
+
+ {excerpt}
+
+ {/if}
+
+
+
+
+
diff --git a/src/lib/components/molecules/Callout.story.svelte b/src/lib/components/molecules/Callout.story.svelte
new file mode 100644
index 0000000..2980d8c
--- /dev/null
+++ b/src/lib/components/molecules/Callout.story.svelte
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus sapien mi, euismod eu
+ ipsum eget, faucibus maximus erat. Integer nisl tellus, interdum sit amet nunc vel,
+ ullamcorper suscipit odio.
+
+
+
+
diff --git a/src/lib/components/molecules/Callout.svelte b/src/lib/components/molecules/Callout.svelte
new file mode 100644
index 0000000..e5e451b
--- /dev/null
+++ b/src/lib/components/molecules/Callout.svelte
@@ -0,0 +1,71 @@
+
+
+
+ {#if type}
+
+ {#if type == 'info'}
+
+ {:else if type == 'warning' || type == 'error'}
+
+ {:else if type == 'success'}
+
+ {/if}
+
+ {/if}
+
+
+
+
diff --git a/src/lib/components/molecules/CodeBlock.story.svelte b/src/lib/components/molecules/CodeBlock.story.svelte
new file mode 100644
index 0000000..1640f16
--- /dev/null
+++ b/src/lib/components/molecules/CodeBlock.story.svelte
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+ < Header />
+
+ < main>
+ < slot />
+ </ main>
+
+ < Footer />
+
+
+
+
diff --git a/src/lib/components/molecules/CodeBlock.svelte b/src/lib/components/molecules/CodeBlock.svelte
new file mode 100644
index 0000000..d63c118
--- /dev/null
+++ b/src/lib/components/molecules/CodeBlock.svelte
@@ -0,0 +1,72 @@
+
+
+
+ {#if filename}
+
{filename}
+ {/if}
+ {#if lang}
+
{lang}
+ {/if}
+
+
+
+
diff --git a/src/lib/components/molecules/FeatureCard.story.svelte b/src/lib/components/molecules/FeatureCard.story.svelte
new file mode 100644
index 0000000..0991750
--- /dev/null
+++ b/src/lib/components/molecules/FeatureCard.story.svelte
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/components/molecules/FeatureCard.svelte b/src/lib/components/molecules/FeatureCard.svelte
new file mode 100644
index 0000000..45c8b69
--- /dev/null
+++ b/src/lib/components/molecules/FeatureCard.svelte
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+ {name}
+
+
{description}
+
+
+
+
+
diff --git a/src/lib/components/molecules/MarkerHighlight.story.svelte b/src/lib/components/molecules/MarkerHighlight.story.svelte
new file mode 100644
index 0000000..b820cfa
--- /dev/null
+++ b/src/lib/components/molecules/MarkerHighlight.story.svelte
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus sapien mi, euismod eu ipsum
+ eget, faucibus maximus erat. Integer nisl tellus, interdum sit amet nunc vel, ullamcorper
+ suscipit odio. Praesent vitae nisl eros. Proin vel bibendum ante. Quisque nec fringilla libero. Duis
+ accumsan urna at arcu vestibulum placerat. Curabitur tincidunt neque mauris, vel posuere ex
+ malesuada quis. Ut nec odio placerat, aliquam elit vitae, volutpat eros. Duis vel sem purus. Donec gravida a
+ lectus vel sagittis. Morbi vel porttitor erat. Vestibulum ante ipsum primis in faucibus orci luctus
+ et ultrices posuere cubilia curae;
+
+
+
diff --git a/src/lib/components/molecules/MarkerHighlight.svelte b/src/lib/components/molecules/MarkerHighlight.svelte
new file mode 100644
index 0000000..d5e55d2
--- /dev/null
+++ b/src/lib/components/molecules/MarkerHighlight.svelte
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
diff --git a/src/lib/components/molecules/Socials.story.svelte b/src/lib/components/molecules/Socials.story.svelte
new file mode 100644
index 0000000..9d828aa
--- /dev/null
+++ b/src/lib/components/molecules/Socials.story.svelte
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/src/lib/components/molecules/Socials.svelte b/src/lib/components/molecules/Socials.svelte
new file mode 100644
index 0000000..a51d205
--- /dev/null
+++ b/src/lib/components/molecules/Socials.svelte
@@ -0,0 +1,73 @@
+
+
+
+
+
diff --git a/src/lib/components/molecules/SparklingHighlight.story.svelte b/src/lib/components/molecules/SparklingHighlight.story.svelte
new file mode 100644
index 0000000..ce07fc9
--- /dev/null
+++ b/src/lib/components/molecules/SparklingHighlight.story.svelte
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {text}
+
+
+
+
diff --git a/src/lib/components/molecules/SparklingHighlight.svelte b/src/lib/components/molecules/SparklingHighlight.svelte
new file mode 100644
index 0000000..088d23a
--- /dev/null
+++ b/src/lib/components/molecules/SparklingHighlight.svelte
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/src/lib/components/molecules/ThemeToggle.svelte b/src/lib/components/molecules/ThemeToggle.svelte
new file mode 100644
index 0000000..44edb38
--- /dev/null
+++ b/src/lib/components/molecules/ThemeToggle.svelte
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Auto
+
+
+
diff --git a/src/lib/components/molecules/TintHighlight.story.svelte b/src/lib/components/molecules/TintHighlight.story.svelte
new file mode 100644
index 0000000..7603376
--- /dev/null
+++ b/src/lib/components/molecules/TintHighlight.story.svelte
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus sapien mi, euismod eu ipsum
+ eget, faucibus maximus erat. Integer nisl tellus, interdum sit amet nunc vel, ullamcorper
+ suscipit odio. Praesent vitae nisl eros. Proin vel bibendum ante. Quisque nec fringilla libero. Duis
+ accumsan urna at arcu vestibulum placerat. Curabitur tincidunt neque mauris, vel posuere ex
+ malesuada quis. Ut nec odio placerat, aliquam elit vitae, volutpat eros. Duis vel sem purus. Donec gravida a
+ lectus vel sagittis. Morbi vel porttitor erat. Vestibulum ante ipsum primis in faucibus orci luctus
+ et ultrices posuere cubilia curae;
+
+
+
diff --git a/src/lib/components/molecules/TintHighlight.svelte b/src/lib/components/molecules/TintHighlight.svelte
new file mode 100644
index 0000000..5bc35c8
--- /dev/null
+++ b/src/lib/components/molecules/TintHighlight.svelte
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/src/lib/components/organisms/About.story.svelte b/src/lib/components/organisms/About.story.svelte
new file mode 100644
index 0000000..fde8faa
--- /dev/null
+++ b/src/lib/components/organisms/About.story.svelte
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/src/lib/components/organisms/About.svelte b/src/lib/components/organisms/About.svelte
new file mode 100644
index 0000000..0ffd948
--- /dev/null
+++ b/src/lib/components/organisms/About.svelte
@@ -0,0 +1,87 @@
+
+
+
+
+
+ This is a catchy headline that
+ sparkles!
+
+
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptatum velit voluptatibus commodi
+ autem provident quam labore, libero beatae praesentium voluptate?
+
+
+ Socials:
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/components/organisms/ContentSection.story.svelte b/src/lib/components/organisms/ContentSection.story.svelte
new file mode 100644
index 0000000..f5e4371
--- /dev/null
+++ b/src/lib/components/organisms/ContentSection.story.svelte
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+ Button
+
+ Content
+
+
+
+
+
+
+ Button
+
+ Content
+
+
+
+
+
+
+ Button
+
+ Content
+
+
+
diff --git a/src/lib/components/organisms/ContentSection.svelte b/src/lib/components/organisms/ContentSection.svelte
new file mode 100644
index 0000000..898a878
--- /dev/null
+++ b/src/lib/components/organisms/ContentSection.svelte
@@ -0,0 +1,104 @@
+
+
+
+
+ {#if title || description}
+
+ {#if title}
+
+ {title}
+
+ {/if}
+ {#if description}
+
+ {description}
+
+ {/if}
+
+ {/if}
+ {#if $$slots['button']}
+
+
+
+ {/if}
+
+
+
+
+
+
+
diff --git a/src/lib/components/organisms/Features.svelte b/src/lib/components/organisms/Features.svelte
new file mode 100644
index 0000000..4db5cf3
--- /dev/null
+++ b/src/lib/components/organisms/Features.svelte
@@ -0,0 +1,73 @@
+
+
+
+
+
+ {#each features as feature}
+
+ {/each}
+
+
+
+
+
diff --git a/src/lib/components/organisms/Footer.story.svelte b/src/lib/components/organisms/Footer.story.svelte
new file mode 100644
index 0000000..caece8b
--- /dev/null
+++ b/src/lib/components/organisms/Footer.story.svelte
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/src/lib/components/organisms/Footer.svelte b/src/lib/components/organisms/Footer.svelte
new file mode 100644
index 0000000..e804966
--- /dev/null
+++ b/src/lib/components/organisms/Footer.svelte
@@ -0,0 +1,70 @@
+
+
+
+
+
diff --git a/src/lib/components/organisms/Header.story.svelte b/src/lib/components/organisms/Header.story.svelte
new file mode 100644
index 0000000..2c50439
--- /dev/null
+++ b/src/lib/components/organisms/Header.story.svelte
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/components/organisms/Header.svelte b/src/lib/components/organisms/Header.svelte
new file mode 100644
index 0000000..9a88171
--- /dev/null
+++ b/src/lib/components/organisms/Header.svelte
@@ -0,0 +1,80 @@
+
+
+
+
+
diff --git a/src/lib/components/organisms/Hero.story.svelte b/src/lib/components/organisms/Hero.story.svelte
new file mode 100644
index 0000000..5a5dbc0
--- /dev/null
+++ b/src/lib/components/organisms/Hero.story.svelte
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/src/lib/components/organisms/Hero.svelte b/src/lib/components/organisms/Hero.svelte
new file mode 100644
index 0000000..0bcb40c
--- /dev/null
+++ b/src/lib/components/organisms/Hero.svelte
@@ -0,0 +1,76 @@
+
+
+
+ This is a SvelteKit Static Blog Template!
+
+ It supports Markdown,
+ and is really fast.
+
+
+
+
+
+ Source Code
+
+
+
+
+ Components
+
+
+
+
+
diff --git a/src/lib/components/organisms/RecentPosts.svelte b/src/lib/components/organisms/RecentPosts.svelte
new file mode 100644
index 0000000..0f8f350
--- /dev/null
+++ b/src/lib/components/organisms/RecentPosts.svelte
@@ -0,0 +1,46 @@
+
+
+
+
+ View More
+
+
+ {#each posts as post}
+
+ {/each}
+
+
+
+
diff --git a/src/lib/components/organisms/RelatedPosts.svelte b/src/lib/components/organisms/RelatedPosts.svelte
new file mode 100644
index 0000000..4980d23
--- /dev/null
+++ b/src/lib/components/organisms/RelatedPosts.svelte
@@ -0,0 +1,44 @@
+
+
+
+
+ {#each posts as post}
+
+ {/each}
+
+
+
+
diff --git a/src/lib/components/organisms/Waves.story.svelte b/src/lib/components/organisms/Waves.story.svelte
new file mode 100644
index 0000000..d5292b3
--- /dev/null
+++ b/src/lib/components/organisms/Waves.story.svelte
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/src/lib/components/organisms/Waves.svelte b/src/lib/components/organisms/Waves.svelte
new file mode 100644
index 0000000..1cb2edd
--- /dev/null
+++ b/src/lib/components/organisms/Waves.svelte
@@ -0,0 +1,93 @@
+
+
+
diff --git a/src/lib/data/blog-posts/index.ts b/src/lib/data/blog-posts/index.ts
new file mode 100644
index 0000000..73fa19f
--- /dev/null
+++ b/src/lib/data/blog-posts/index.ts
@@ -0,0 +1,4 @@
+import { filterPosts, importPosts } from './utils';
+
+export const allPosts = importPosts(true);
+export const filteredPosts = filterPosts(allPosts);
\ No newline at end of file
diff --git a/src/lib/data/blog-posts/utils.ts b/src/lib/data/blog-posts/utils.ts
new file mode 100644
index 0000000..a8b59b8
--- /dev/null
+++ b/src/lib/data/blog-posts/utils.ts
@@ -0,0 +1,72 @@
+// Disabling eslint because importing Prism is needed
+// even if not directly used in this file
+// eslint-disable-next-line no-unused-vars
+import Prism from 'prismjs';
+// Here we assign it to a variable so the import above
+// is not removed automatically on build
+const ifYouRemoveMeTheBuildFails = Prism;
+import 'prism-svelte';
+import readingTime from 'reading-time/lib/reading-time';
+import striptags from 'striptags';
+import type { BlogPost } from "$lib/utils/types";
+
+export const importPosts = (render = false) => {
+ const blogImports = import.meta.glob('$routes/*/*/*.md', { eager: true });
+ const innerImports = import.meta.glob('$routes/*/*/*/*.md', { eager: true });
+
+ const imports = { ...blogImports, ...innerImports };
+
+ const posts: BlogPost[] = [];
+ for (const path in imports) {
+ const post = imports[path] as any;
+ if (post) {
+ posts.push({
+ ...post.metadata,
+ html: render && post.default.render ? post.default.render()?.html : undefined,
+ });
+ }
+ }
+
+ return posts;
+}
+
+export const filterPosts = (posts: BlogPost[]) => {
+ return posts.filter((post) => !post.hidden)
+ .sort((a, b) =>
+ new Date(a.date).getTime() > new Date(b.date).getTime()
+ ? -1
+ : new Date(a.date).getTime() < new Date(b.date).getTime()
+ ? 1
+ : 0
+ )
+ .map((post) => {
+ const readingTimeResult = post.html ? readingTime(striptags(post.html) || '') : undefined;
+ const relatedPosts = getRelatedPosts(posts, post);
+
+ return {
+ ...post,
+ readingTime: readingTimeResult ? readingTimeResult.text : '',
+ relatedPosts: relatedPosts,
+ } as BlogPost;
+ });
+}
+
+// #region Unexported Functions
+
+const getRelatedPosts = (posts: BlogPost[], post: BlogPost) => {
+ // Get the first 3 posts that have the highest number of tags in common
+ const relatedPosts = posts
+ .filter((p) => p.slug !== post.slug)
+ .sort((a, b) => {
+ const aTags = a.tags?.filter((t) => post.tags?.includes(t));
+ const bTags = b.tags?.filter((t) => post.tags?.includes(t));
+ return aTags?.length > bTags?.length ? -1 : aTags?.length < bTags?.length ? 1 : 0;
+ })
+
+ return relatedPosts.slice(0, 3).map((p) => ({
+ ...p,
+ readingTime: p.html ? readingTime(striptags(p.html) || '').text : '',
+ }));
+}
+
+// #endregion
\ No newline at end of file
diff --git a/src/lib/data/features.ts b/src/lib/data/features.ts
new file mode 100644
index 0000000..eba2d6f
--- /dev/null
+++ b/src/lib/data/features.ts
@@ -0,0 +1,43 @@
+import type { Feature } from "$lib/utils/types";
+
+export default [
+ {
+ name: 'Markdown Support',
+ description:
+ 'Blog posts are written in Markdown, a simple and nearly-universal format. This means you can bring over your posts from other platforms, and easily export to another if you want to.',
+ image: 'images/features/markdown.jpg',
+ tags: [{ label: 'Powered by MDsveX' }]
+ },
+ {
+ name: 'Themeable',
+ description:
+ 'You can easily theme the entire website by changing just a few colors in the _themes.scss file.',
+ image: 'images/features/themeable.jpg',
+ tags: [{ label: 'Primary Color' }, { label: 'Secondary Color', color: 'secondary' }]
+ },
+ {
+ name: 'Extensible',
+ description:
+ 'Components are built to be reused, and you can build new pages and layouts without much CSS knowledge. You can see all components in Histoire by running "npm run story:dev"',
+ image: 'images/features/extensible.jpg',
+ },
+ {
+ name: 'Well Optimized',
+ description:
+ 'Images are automatically optimized and lazy loaded, to ensure the website loads as fast as possible regardless of connection speed.',
+ image: 'images/features/optimized.jpg',
+ tags: [{ label: 'Powered by Image Transmutation' }]
+ },
+ {
+ name: 'Light and Dark Modes',
+ description:
+ 'This template was built with dark mode in mind. It can swap between themes automatically (based on system settings) or manually. Both themes can be tweaked in the _themes.scss file.',
+ image: 'images/features/light-dark.jpg',
+ },
+ {
+ name: 'Open Source',
+ description:
+ 'All code is open source, which means you can copy and modify it to your heart\'s content. All I ask is that you make your code open too so that knowledge can be passed on.',
+ image: 'images/features/open-source.jpg'
+ },
+] as Feature[];
\ No newline at end of file
diff --git a/src/lib/data/meta.ts b/src/lib/data/meta.ts
new file mode 100644
index 0000000..2266846
--- /dev/null
+++ b/src/lib/data/meta.ts
@@ -0,0 +1,21 @@
+// Base values for meta tags
+// So they can be added as suffixes on different pages
+// Via
+
+export const siteBaseUrl = 'https://sveltekit-static-blog-template.vercel.app/';
+
+export const keywords = [
+ 'Svelte',
+ 'SvelteKit',
+ 'Template',
+ 'Blog',
+ 'Starter',
+ 'Static Site'
+];
+
+export const description =
+ "A light, neat, and easy-to-use SvelteKit template for your next website.";
+
+export const title = 'SvelteKit Static Blog Template';
+
+export const image = `${siteBaseUrl}/images/site-preview.png`;
diff --git a/src/lib/icons/alert.svelte b/src/lib/icons/alert.svelte
new file mode 100644
index 0000000..29e7367
--- /dev/null
+++ b/src/lib/icons/alert.svelte
@@ -0,0 +1,22 @@
+
+
+
+
+
diff --git a/src/lib/icons/blog.svelte b/src/lib/icons/blog.svelte
new file mode 100644
index 0000000..cde82f2
--- /dev/null
+++ b/src/lib/icons/blog.svelte
@@ -0,0 +1,16 @@
+
+
+
+
diff --git a/src/lib/icons/chat.svelte b/src/lib/icons/chat.svelte
new file mode 100644
index 0000000..f7455bd
--- /dev/null
+++ b/src/lib/icons/chat.svelte
@@ -0,0 +1,36 @@
+
+
+
+
+
+
diff --git a/src/lib/icons/check.svelte b/src/lib/icons/check.svelte
new file mode 100644
index 0000000..fec37d4
--- /dev/null
+++ b/src/lib/icons/check.svelte
@@ -0,0 +1,21 @@
+
+
+
+
diff --git a/src/lib/icons/circle.svelte b/src/lib/icons/circle.svelte
new file mode 100644
index 0000000..fb96e6a
--- /dev/null
+++ b/src/lib/icons/circle.svelte
@@ -0,0 +1,15 @@
+
+
+
diff --git a/src/lib/icons/download.svelte b/src/lib/icons/download.svelte
new file mode 100644
index 0000000..2f2d468
--- /dev/null
+++ b/src/lib/icons/download.svelte
@@ -0,0 +1,16 @@
+
+
+
+
diff --git a/src/lib/icons/error.svelte b/src/lib/icons/error.svelte
new file mode 100644
index 0000000..717b297
--- /dev/null
+++ b/src/lib/icons/error.svelte
@@ -0,0 +1,138 @@
+
diff --git a/src/lib/icons/experience.svelte b/src/lib/icons/experience.svelte
new file mode 100644
index 0000000..977a1ac
--- /dev/null
+++ b/src/lib/icons/experience.svelte
@@ -0,0 +1,14 @@
+
+
+
diff --git a/src/lib/icons/external-link.svelte b/src/lib/icons/external-link.svelte
new file mode 100644
index 0000000..74fb130
--- /dev/null
+++ b/src/lib/icons/external-link.svelte
@@ -0,0 +1,21 @@
+
+
+
+
diff --git a/src/lib/icons/features.svelte b/src/lib/icons/features.svelte
new file mode 100644
index 0000000..c423f51
--- /dev/null
+++ b/src/lib/icons/features.svelte
@@ -0,0 +1,16 @@
+
+
+
diff --git a/src/lib/icons/footer-wave.svelte b/src/lib/icons/footer-wave.svelte
new file mode 100644
index 0000000..93114c6
--- /dev/null
+++ b/src/lib/icons/footer-wave.svelte
@@ -0,0 +1,15 @@
+
diff --git a/src/lib/icons/info.svelte b/src/lib/icons/info.svelte
new file mode 100644
index 0000000..afcfaae
--- /dev/null
+++ b/src/lib/icons/info.svelte
@@ -0,0 +1,22 @@
+
+
+
+
+
diff --git a/src/lib/icons/internet.svelte b/src/lib/icons/internet.svelte
new file mode 100644
index 0000000..facdf20
--- /dev/null
+++ b/src/lib/icons/internet.svelte
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/icons/pin.svelte b/src/lib/icons/pin.svelte
new file mode 100644
index 0000000..1aa2467
--- /dev/null
+++ b/src/lib/icons/pin.svelte
@@ -0,0 +1,21 @@
+
+
+
+
diff --git a/src/lib/icons/rss.svelte b/src/lib/icons/rss.svelte
new file mode 100644
index 0000000..b0fdbbf
--- /dev/null
+++ b/src/lib/icons/rss.svelte
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/src/lib/icons/socials/email.svelte b/src/lib/icons/socials/email.svelte
new file mode 100644
index 0000000..b2f014e
--- /dev/null
+++ b/src/lib/icons/socials/email.svelte
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/src/lib/icons/socials/github.svelte b/src/lib/icons/socials/github.svelte
new file mode 100644
index 0000000..f49f238
--- /dev/null
+++ b/src/lib/icons/socials/github.svelte
@@ -0,0 +1,21 @@
+
+
+
+
diff --git a/src/lib/icons/socials/linkedin.svelte b/src/lib/icons/socials/linkedin.svelte
new file mode 100644
index 0000000..a8ba627
--- /dev/null
+++ b/src/lib/icons/socials/linkedin.svelte
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/src/lib/icons/socials/mastodon.svelte b/src/lib/icons/socials/mastodon.svelte
new file mode 100644
index 0000000..14efada
--- /dev/null
+++ b/src/lib/icons/socials/mastodon.svelte
@@ -0,0 +1,16 @@
+
diff --git a/src/lib/icons/socials/telegram.svelte b/src/lib/icons/socials/telegram.svelte
new file mode 100644
index 0000000..8b96e18
--- /dev/null
+++ b/src/lib/icons/socials/telegram.svelte
@@ -0,0 +1,15 @@
+
+
+
diff --git a/src/lib/icons/star.svelte b/src/lib/icons/star.svelte
new file mode 100644
index 0000000..394d743
--- /dev/null
+++ b/src/lib/icons/star.svelte
@@ -0,0 +1,15 @@
+
+
+
diff --git a/src/lib/utils/regex.ts b/src/lib/utils/regex.ts
new file mode 100644
index 0000000..99fb8a3
--- /dev/null
+++ b/src/lib/utils/regex.ts
@@ -0,0 +1 @@
+export const HttpRegex = /^((http|https):\/\/)/;
\ No newline at end of file
diff --git a/src/lib/utils/types.ts b/src/lib/utils/types.ts
new file mode 100644
index 0000000..ce2bc59
--- /dev/null
+++ b/src/lib/utils/types.ts
@@ -0,0 +1,40 @@
+export type NoUndefinedField = { [P in keyof T]-?: NoUndefinedField> };
+
+export type SparkleType = {
+ id: string,
+ createdAt: number,
+ color: string,
+ size: number,
+ style: any
+}
+
+export type TagType = {
+ label: string,
+ color?: 'primary' | 'secondary'
+}
+
+export type SocialLink = {
+
+}
+
+export type Feature = {
+ name: string,
+ description: string,
+ image: string,
+ tags: TagType[]
+}
+
+export type BlogPost = {
+ tags: string[],
+ keywords: string[],
+ hidden: boolean,
+ slug: string,
+ title: string,
+ date: string,
+ updated: string,
+ excerpt: string,
+ html: string | undefined,
+ readingTime: string,
+ relatedPosts: BlogPost[],
+ coverImage: string | undefined
+}
\ No newline at end of file
diff --git a/src/routes/(blog-article)/+layout.server.ts b/src/routes/(blog-article)/+layout.server.ts
new file mode 100644
index 0000000..de03200
--- /dev/null
+++ b/src/routes/(blog-article)/+layout.server.ts
@@ -0,0 +1,11 @@
+import { filteredPosts } from '$lib/data/blog-posts';
+
+export async function load({ url }: { url: { pathname: string } }) {
+ const { pathname } = url;
+ const slug = pathname.replace('/', '');
+ const post = filteredPosts.find((post) => post.slug === slug);
+
+ return {
+ post
+ };
+}
diff --git a/src/routes/(blog-article)/+layout.svelte b/src/routes/(blog-article)/+layout.svelte
new file mode 100644
index 0000000..460ba07
--- /dev/null
+++ b/src/routes/(blog-article)/+layout.svelte
@@ -0,0 +1,190 @@
+
+
+
+ {#if post}
+
+
+
+
+
+
+
+ {post.title} - {title}
+
+
+
+ {#if post.coverImage}
+
+
+ {/if}
+ {/if}
+
+
+
+
+
+
+
+
+ {#if post && post.coverImage}
+
+
+
+ {/if}
+
+
+
+
+
+ {#if post.relatedPosts && post.relatedPosts.length > 0}
+
+
+
+ {/if}
+
+
+
+
+
+
diff --git a/src/routes/(blog-article)/blog-posts/+page.md b/src/routes/(blog-article)/blog-posts/+page.md
new file mode 100644
index 0000000..5c03c39
--- /dev/null
+++ b/src/routes/(blog-article)/blog-posts/+page.md
@@ -0,0 +1,67 @@
+---
+title: How Blog Posts Work
+slug: blog-posts
+coverImage: /images/posts/blog-posts.jpg
+date: 2023-04-22T21:55:15.361Z
+excerpt: How to manage existing blog posts and create new ones
+tags:
+ - Documentation
+---
+
+
+
+All blog posts are located inside the `src/routes/(blog-article)` folder. Each folder inside it represents a blog post, and each folder has a `+page.md` file, which is the file that contains the post's content.
+
+This way, the URL for each blog post is generated with the folder's name. For example, the folder `src/routes/(blog-article)/how-blog-posts-work` will generate the URL `https://mysite.com/how-blog-posts-work`.
+
+All posts are Markdown files, which means you can use the [Markdown syntax](https://www.markdownguide.org/basic-syntax) in them, and it will work out of the box. However, since this projects uses [MDsveX](https://mdsvex.pngwn.io/) to parse Markdown, you can also use Svelte components inside them! This means that the components used in other pages can also be used in blog posts.
+
+
+ This is a Svelte component inside a Markdown file!
+
+
+## Processing
+
+Besides the blog post page itself, the blog posts can be displayed in other places, such as the `/blog` page, which lists all blog posts, and the `` component, used in the home page.
+
+To be able to do that, posts are processed in the `src/lib/data/blog-posts/index.ts` file. That file imports the blog post files and processes them, so we're able to use some of the post's metadata to list them. For example, we get the post's title, cover image, and calculate the reading time based on its content, so that information is displayed in the blog post cards in the `/blog` page.
+
+There is also some basic logic to get related posts based on a post's tags. The logic should be straightforward enough to modify it to your needs.
+
+## Creating a new post
+
+To create a new post, create a new folder inside the `src/routes/(blog-article)` folder, and inside it, create a `+page.md` file. The folder name will be used as the post's URL slug, so make sure it's a valid URL slug.
+
+Inside the `+page.md` file, you must start with the front matter, which is a YAML-like syntax that is used to define metadata for the post. The front matter must be the first thing in the file, and must be separated from the rest of the content by three dashes (`---`). An example of a front matter is:
+
+
+
+```md
+---
+slug: my-new-blog-post
+title: My New Blog Post
+date: 2023-04-22T20:45:25.350Z
+excerpt: A short description of the post
+coverImage: /images/posts/cover-image.jpg
+tags:
+ - Example
+---
+```
+
+
+
+## Managing blog posts
+
+I highly recommend the [Front Matter VS Code extension](https://frontmatter.codes/) to manage blog posts. It gives you a nice CMS-like UI to manage the front matter of all blog posts, as well as a preview of their content. It is, of course, optional, and you can manage everything directly in the Markdown files if you prefer.
+
+
+
+
+
+## RSS
+
+This template automatically generates a RSS feed of your blog posts. It is generated in the `src/routes/rss.xml/+server.ts` file, and it is available at the `/rss.xml` URL.
diff --git a/src/routes/(blog-article)/customization/+page.md b/src/routes/(blog-article)/customization/+page.md
new file mode 100644
index 0000000..7ad2b23
--- /dev/null
+++ b/src/routes/(blog-article)/customization/+page.md
@@ -0,0 +1,60 @@
+---
+slug: customization
+title: How to Customize this Template
+date: 2023-04-22T21:55:27.154Z
+excerpt: How to customize what you're seeing here and make it your own.
+coverImage: /images/posts/customization.jpg
+tags:
+ - Documentation
+---
+
+In general, content can be modified by editing the **organisms** and the pages themselves. Below is a list of the most common changes that you may want to make.
+
+## Domain/site URL
+
+The first thing you might want to do is replace the domain of of the site with your own. There are two places where you need to do that:
+
+- In the `package.json` file, check the `postbuild` script. Change the domain there to your own, so it ends up like this: `svelte-sitemap --domain https://your-domain.com`. This is used to generate the sitemap of your website, which is used by search engines to index your site.
+- In the `src/lib/data/meta.ts` file, change the `siteBaseUrl` property to your own domain. This is used in multiple parts of the app wherever the site needs to link to itself.
+
+## Header/site logo
+
+To replace the logo that appears in the header, modify or replace the contents of the `Logo.svelte` atom.
+
+The links that appear on the header can be modified directly in the `Header.svelte` organism.
+
+## Hero section
+
+The hero section is the first section of the site's home page. It is composed of a Heading, the _intro_ text, and a list of buttons/CTAs. The contents of this section can be modified directly in the `Hero.svelte` organism.
+
+## About section
+
+The about section contains another headline, a paragraph of text, some social media links, and optionally an image. The contents of this section can be modified directly in the `About.svelte` organism.
+
+## Social Links
+
+The social links are contained in the `Socials.svelte` molecule and can be used in any page. This template shows them on the About section and in the Footer.
+
+## Footer
+
+The footer contains some credits, a list of social links, and the RSS/Theme toggle. The contents of this section can be modified directly in the `Footer.svelte` organism.
+
+## Colors
+
+You can change the color palette of the website just by tweaking the `_themes.scss` file. The file uses the `define-color` scss function to automatically set the color variables in Hex, RGB and HSL formats, so you can choose whichever format you need.
+
+The main theme colors (primary and secondary) have two variants: shade and tint. The shade is a lighter version of the color (darker in dark mode), and the tint should almost match the page's background, so that in light mode, it's really bright, and in dark mode, it's really dark.
+
+## Fonts
+
+This template uses the Inter, Merriweather and Ubuntu Mono font families. You can change the font family by editing the `_variables.scss` file, and the code is already set up to accept a default font, a heading font, and a monospace font.
+
+I recommend using [Fontsource](https://fontsource.org/) to import the fonts you need, or self-hosting them. In case you're using Fontsource, you can import the fonts in `global.scss` file to make sure they're available in the entire site.
+
+## Favicon
+
+Favicons are located in the `static/favicons` folder. I like to use [Real Favicon Generator](https://realfavicongenerator.net) to generate mine, but you can use any other tool you like. I wrote [a blog post about Favicons](https://fantinel.dev/fixing-favicons) in case you want to learn more about them.
+
+## Social Media Link Preview
+
+You probably want to customize how links to your website look when posted on social media/messaging apps. To do that, you can edit the info in `src/lib/data/meta.ts`. There, you can edit the site's title, description, tags (used by search engines) and the image that appears when sharing a link.
\ No newline at end of file
diff --git a/src/routes/(blog-article)/project-structure/+page.md b/src/routes/(blog-article)/project-structure/+page.md
new file mode 100644
index 0000000..9ab749b
--- /dev/null
+++ b/src/routes/(blog-article)/project-structure/+page.md
@@ -0,0 +1,57 @@
+---
+slug: project-structure
+title: Project Structure
+date: 2023-04-22T21:55:21.800Z
+excerpt: How code and structure are organized.
+coverImage: /images/posts/project-structure.jpg
+tags:
+ - Documentation
+---
+
+
+
+This project follows the basic [SvelteKit structure](https://kit.svelte.dev/docs/project-structure), with some added conventions to make customization a post-management easier.
+
+## Components
+
+The components are organized following the [Atomic Design](https://medium.com/@WeAreMobile1st/atomic-design-getting-started-916bc81bad0e) principles, albeit somewhat simplified. Components are in the `src/lib/components` folder, and are organized in the following way:
+
+### Atoms
+
+Atoms are the most basic components, which can be seen as building blocks for other components. They're small and self-contained, and do only one thing. Examples of atoms are buttons, inputs, and tags.
+
+### Molecules
+
+Molecules are groups of atoms that work together to form a more complex component. Examples of molecules are cards, groups of links, or an alert callout.
+
+### Organisms
+
+Organisms, in this project, are code blocks that represent a section of a page, such as the header, footer and hero section. They can be used directly in a page as a sort of building block, so the page's code can be as simple as this:
+
+
+
+```html
+
+
+
+
+```
+
+
+
+## Component Gallery
+
+This project uses [Histoire](https://histoire.dev) to be able to see and develop components in isolation. To open it, run `npm run story:dev`. This way you can customize and build new components with placeholder content and without worrying about where to put them in a page.
+
+## Pages
+
+Pages obey the default SvelteKit structure, but can be summarized as follows:
+
+- There are two different file types: the pages (`+page.svelte`) and the layouts (`+layout.svelte`). Layouts are a way to reuse the same structure between different pages without having to duplicate code;
+- There are two different page layouts in this site: the "Waves" layout, which all pages inside the `(waves)` folder use, and the "Blog Article" layout, which all pages inside the `(blog-article)` folder use;
+
+## Blog Posts
+
+To know how blog posts work and how to create new ones, check out [How Blog Posts Work](/blog-posts).
\ No newline at end of file
diff --git a/src/routes/(waves)/+layout.svelte b/src/routes/(waves)/+layout.svelte
new file mode 100644
index 0000000..9b50c4a
--- /dev/null
+++ b/src/routes/(waves)/+layout.svelte
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/routes/(waves)/+page.server.ts b/src/routes/(waves)/+page.server.ts
new file mode 100644
index 0000000..7c2ce35
--- /dev/null
+++ b/src/routes/(waves)/+page.server.ts
@@ -0,0 +1,11 @@
+import features from '$lib/data/features';
+import { filteredPosts } from '$lib/data/blog-posts';
+
+export async function load() {
+ const posts = filteredPosts.slice(0, 4);
+
+ return {
+ features,
+ posts
+ };
+}
diff --git a/src/routes/(waves)/+page.svelte b/src/routes/(waves)/+page.svelte
new file mode 100644
index 0000000..6aca299
--- /dev/null
+++ b/src/routes/(waves)/+page.svelte
@@ -0,0 +1,23 @@
+
+
+
+
+
+ {#if posts && posts.length > 0}
+
+ {/if}
+
+
diff --git a/src/routes/(waves)/404/+page.svelte b/src/routes/(waves)/404/+page.svelte
new file mode 100644
index 0000000..5b433d6
--- /dev/null
+++ b/src/routes/(waves)/404/+page.svelte
@@ -0,0 +1,41 @@
+
+
+
+
+
Oh no! 404!
+
+
+
+
It seems like coffee was spilled all over this page, and now it can't be displayed.
+
+
Start over
+
+
+
+
diff --git a/src/routes/(waves)/blog/+page.server.ts b/src/routes/(waves)/blog/+page.server.ts
new file mode 100644
index 0000000..455adae
--- /dev/null
+++ b/src/routes/(waves)/blog/+page.server.ts
@@ -0,0 +1,7 @@
+import { filteredPosts } from '$lib/data/blog-posts';
+
+export async function load() {
+ return {
+ posts: filteredPosts
+ };
+}
diff --git a/src/routes/(waves)/blog/+page.svelte b/src/routes/(waves)/blog/+page.svelte
new file mode 100644
index 0000000..2c25a32
--- /dev/null
+++ b/src/routes/(waves)/blog/+page.svelte
@@ -0,0 +1,68 @@
+
+
+
+
+
+ {#each posts as post}
+
+ {/each}
+
+
+
+
+
diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte
new file mode 100644
index 0000000..257db17
--- /dev/null
+++ b/src/routes/+error.svelte
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
Oh no!
+
+
+
+
It seems like coffee was spilled all over this page, and now it can't be displayed.
+
+
Start over
+
+
+
+
+
+
+
diff --git a/src/routes/+page.ts b/src/routes/+layout.ts
similarity index 100%
rename from src/routes/+page.ts
rename to src/routes/+layout.ts
diff --git a/src/routes/+page.svelte b/src/routes/oldpage.svelte
similarity index 100%
rename from src/routes/+page.svelte
rename to src/routes/oldpage.svelte
diff --git a/src/routes/rss.xml/+server.ts b/src/routes/rss.xml/+server.ts
new file mode 100644
index 0000000..9be722c
--- /dev/null
+++ b/src/routes/rss.xml/+server.ts
@@ -0,0 +1,72 @@
+import { description, siteBaseUrl, title } from '$lib/data/meta';
+import type { BlogPost } from '$lib/utils/types';
+import dateformat from 'dateformat';
+import { filterPosts, importPosts } from '$lib/data/blog-posts/utils';
+
+export const prerender = true;
+
+export async function GET() {
+ const allPosts = importPosts(true);
+ const filteredPosts = filterPosts(allPosts);
+
+ const body = xml(filteredPosts);
+ const headers = {
+ 'Cache-Control': 'max-age=0, s-maxage=3600',
+ 'Content-Type': 'application/xml'
+ };
+ return new Response(body, { headers });
+}
+
+const xml = (posts: BlogPost[]) => `
+
+
+
+ ${title}
+ ${siteBaseUrl}
+ ${description}
+
+ ${siteBaseUrl}/favicons/favicon-32x32.png
+ ${title}
+ ${siteBaseUrl}
+ 32
+ 32
+
+ ${posts
+ .map(
+ (post) => `
+ -
+
${siteBaseUrl}/${post.slug}
+ ${post.title}
+ ${post.excerpt}
+ ${siteBaseUrl}/${post.slug}
+ ${dateformat(post.date, 'ddd, dd mmm yyyy HH:MM:ss o')}
+ ${post.tags ? post.tags.map((tag) => `${tag} `).join('') : ''}
+
+ If anything looks wrong,
+
+
+ read on the site!
+
+
+
+
+ ${post.html}
+ ]]>
+ ${post.coverImage ? ` ` : ''}
+ ${post.coverImage ? ` ` : ''}
+
+ `
+ )
+ .join('')}
+
+ `;
\ No newline at end of file
diff --git a/svelte.config.js b/svelte.config.js
index d7d7710..3e276f7 100644
--- a/svelte.config.js
+++ b/svelte.config.js
@@ -1,5 +1,11 @@
import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/kit/vite';
+import { mdsvex } from 'mdsvex';
+import rehypeExternalLinks from 'rehype-external-links';
+import rehypeSlug from 'rehype-slug';
+import rehypeAutolinkHeadings from 'rehype-autolink-headings';
+
+const extensions = ['.svelte', '.md'];
/** @type {import('@sveltejs/kit').Config} */
const config = {
@@ -10,6 +16,31 @@ const config = {
kit: {
adapter: adapter()
},
+ preprocess: [
+ vitePreprocess(),
+ mdsvex({
+ extensions: extensions,
+ rehypePlugins: [
+ rehypeExternalLinks, // Adds 'target' and 'rel' to external links
+ rehypeSlug, // Adds 'id' attributes to Headings (h1,h2,etc)
+ [
+ rehypeAutolinkHeadings,
+ {
+ // Adds hyperlinks to the headings, requires rehypeSlug
+ behavior: 'prepend',
+ properties: { className: ['heading-link'], title: 'Permalink', ariaHidden: 'true' },
+ content: {
+ type: 'element',
+ tagName: 'span',
+ properties: {},
+ children: [{ type: 'text', value: '#' }]
+ }
+ }
+ ]
+ ]
+ })
+ ],
+ extensions: extensions,
trailingSlash: 'always'
};
diff --git a/vite.config.ts b/vite.config.ts
index 37b6a84..a226d65 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,9 +1,15 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vitest/config';
+import path from 'path';
export default defineConfig({
plugins: [sveltekit()],
test: {
include: ['src/**/*.{test,spec}.{js,ts}']
+ },
+ resolve: {
+ alias: {
+ $routes: path.resolve('./src/routes')
+ }
}
});