Add blog post rendering

main
trivernis 3 months ago
parent 1e11512b21
commit 5f17c0451c
WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS.
GPG Key ID: 7E6D18B61C8D2F4B

60
package-lock.json generated

@ -10,7 +10,8 @@
"dependencies": { "dependencies": {
"moment": "^2.30.1", "moment": "^2.30.1",
"qs": "^6.12.2", "qs": "^6.12.2",
"sanitize.css": "^13.0.0" "sanitize.css": "^13.0.0",
"svelte-markdown": "^0.4.1"
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-auto": "^3.0.0",
@ -31,7 +32,6 @@
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
"dev": true,
"dependencies": { "dependencies": {
"@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.24" "@jridgewell/trace-mapping": "^0.3.24"
@ -429,7 +429,6 @@
"version": "0.3.5", "version": "0.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
"integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
"dev": true,
"dependencies": { "dependencies": {
"@jridgewell/set-array": "^1.2.1", "@jridgewell/set-array": "^1.2.1",
"@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/sourcemap-codec": "^1.4.10",
@ -443,7 +442,6 @@
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"
} }
@ -452,7 +450,6 @@
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
"dev": true,
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"
} }
@ -460,14 +457,12 @@
"node_modules/@jridgewell/sourcemap-codec": { "node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15", "version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
"dev": true
}, },
"node_modules/@jridgewell/trace-mapping": { "node_modules/@jridgewell/trace-mapping": {
"version": "0.3.25", "version": "0.3.25",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
"dev": true,
"dependencies": { "dependencies": {
"@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
@ -964,8 +959,12 @@
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
"dev": true },
"node_modules/@types/marked": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/@types/marked/-/marked-5.0.2.tgz",
"integrity": "sha512-OucS4KMHhFzhz27KxmWg7J+kIYqyqoW5kdIEI319hqARQQUTqhao3M/F+uFnDXD0Rg72iDDZxZNxq5gvctmLlg=="
}, },
"node_modules/@types/pug": { "node_modules/@types/pug": {
"version": "2.0.10", "version": "2.0.10",
@ -995,7 +994,6 @@
"version": "8.12.1", "version": "8.12.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
"dev": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@ -1044,7 +1042,6 @@
"version": "5.3.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
"dev": true,
"dependencies": { "dependencies": {
"dequal": "^2.0.3" "dequal": "^2.0.3"
} }
@ -1053,7 +1050,6 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz",
"integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==", "integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==",
"dev": true,
"dependencies": { "dependencies": {
"dequal": "^2.0.3" "dequal": "^2.0.3"
} }
@ -1165,7 +1161,6 @@
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz",
"integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==",
"dev": true,
"dependencies": { "dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15", "@jridgewell/sourcemap-codec": "^1.4.15",
"@types/estree": "^1.0.1", "@types/estree": "^1.0.1",
@ -1231,7 +1226,6 @@
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
"dev": true,
"dependencies": { "dependencies": {
"mdn-data": "2.0.30", "mdn-data": "2.0.30",
"source-map-js": "^1.0.1" "source-map-js": "^1.0.1"
@ -1286,7 +1280,6 @@
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
"dev": true,
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
@ -1391,7 +1384,6 @@
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
"dev": true,
"dependencies": { "dependencies": {
"@types/estree": "^1.0.0" "@types/estree": "^1.0.0"
} }
@ -1700,7 +1692,6 @@
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz",
"integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==",
"dev": true,
"dependencies": { "dependencies": {
"@types/estree": "*" "@types/estree": "*"
} }
@ -1741,8 +1732,7 @@
"node_modules/locate-character": { "node_modules/locate-character": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="
"dev": true
}, },
"node_modules/lru-cache": { "node_modules/lru-cache": {
"version": "10.3.1", "version": "10.3.1",
@ -1757,16 +1747,25 @@
"version": "0.30.10", "version": "0.30.10",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz",
"integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==",
"dev": true,
"dependencies": { "dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15" "@jridgewell/sourcemap-codec": "^1.4.15"
} }
}, },
"node_modules/marked": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/marked/-/marked-5.1.2.tgz",
"integrity": "sha512-ahRPGXJpjMjwSOlBoTMZAK7ATXkli5qCPxZ21TG44rx1KEo44bii4ekgTDQPNRQ4Kh7JMb9Ub1PVk1NxRSsorg==",
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 16"
}
},
"node_modules/mdn-data": { "node_modules/mdn-data": {
"version": "2.0.30", "version": "2.0.30",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="
"dev": true
}, },
"node_modules/mdsvex": { "node_modules/mdsvex": {
"version": "0.11.2", "version": "0.11.2",
@ -1963,7 +1962,6 @@
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz",
"integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==",
"dev": true,
"dependencies": { "dependencies": {
"@types/estree": "^1.0.0", "@types/estree": "^1.0.0",
"estree-walker": "^3.0.0", "estree-walker": "^3.0.0",
@ -2273,7 +2271,6 @@
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -2402,7 +2399,6 @@
"version": "4.2.18", "version": "4.2.18",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.18.tgz", "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.18.tgz",
"integrity": "sha512-d0FdzYIiAePqRJEb90WlJDkjUEx42xhivxN8muUBmfZnP+tzUgz12DJ2hRJi8sIHCME7jeK1PTMgKPSfTd8JrA==", "integrity": "sha512-d0FdzYIiAePqRJEb90WlJDkjUEx42xhivxN8muUBmfZnP+tzUgz12DJ2hRJi8sIHCME7jeK1PTMgKPSfTd8JrA==",
"dev": true,
"dependencies": { "dependencies": {
"@ampproject/remapping": "^2.2.1", "@ampproject/remapping": "^2.2.1",
"@jridgewell/sourcemap-codec": "^1.4.15", "@jridgewell/sourcemap-codec": "^1.4.15",
@ -2455,6 +2451,18 @@
"svelte": "^3.19.0 || ^4.0.0" "svelte": "^3.19.0 || ^4.0.0"
} }
}, },
"node_modules/svelte-markdown": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/svelte-markdown/-/svelte-markdown-0.4.1.tgz",
"integrity": "sha512-pOlLY6EruKJaWI9my/2bKX8PdTeP5CM0s4VMmwmC2prlOkjAf+AOmTM4wW/l19Y6WZ87YmP8+ZCJCCwBChWjYw==",
"dependencies": {
"@types/marked": "^5.0.1",
"marked": "^5.1.2"
},
"peerDependencies": {
"svelte": "^4.0.0"
}
},
"node_modules/svelte-preprocess": { "node_modules/svelte-preprocess": {
"version": "5.1.4", "version": "5.1.4",
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.4.tgz", "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.4.tgz",

@ -27,6 +27,7 @@
"dependencies": { "dependencies": {
"moment": "^2.30.1", "moment": "^2.30.1",
"qs": "^6.12.2", "qs": "^6.12.2",
"sanitize.css": "^13.0.0" "sanitize.css": "^13.0.0",
"svelte-markdown": "^0.4.1"
} }
} }

@ -0,0 +1,7 @@
<script lang="ts">
import SvelteMarkdown from "svelte-markdown";
export let markdown: string;
</script>
<SvelteMarkdown source={markdown}/>

@ -1,34 +1,32 @@
<script lang="ts"> <script lang="ts">
import type { ImageMetadata } from "$lib";
import "$lib/vars.scss"; import "$lib/vars.scss";
import { onMount } from "svelte"; import { onMount } from "svelte";
export let sources: { export let imageData: ImageMetadata;
src: string;
type: string;
}[];
export let caption: string | undefined = undefined; let alt: string | undefined = undefined;
export let alt: string | undefined = undefined;
export let border = true;
onMount(() => { onMount(() => {
alt = alt =
alt ?? caption ?? "Unfortunately theres no description for this image."; imageData.altText ??
imageData.caption ??
"Unfortunately theres no description for this image.";
}); });
</script> </script>
<div class="image" class:border={border}> <div class="image">
<figure> <figure>
<picture> <picture>
{#each sources as source} {#each imageData.formats as format}
<source type={source.type} srcset={source.src} /> <source media={format.width? `(min-width: ${format.width * 2}px)` : "(min-width: 0px)"} type={format.mime} srcset={format.url} />
{/each} {/each}
<img src={sources[0]?.src} {alt} class:rounded={!border} /> <img src={imageData.formats[0]?.url} {alt} />
</picture> </picture>
{#if caption} {#if imageData.caption}
<figcaption> <figcaption>
{caption} {imageData.caption}
</figcaption> </figcaption>
{/if} {/if}
</figure> </figure>
@ -37,35 +35,24 @@
<style lang="scss"> <style lang="scss">
@layer component { @layer component {
.image { .image {
&.border { border-radius: 10px;
border-radius: 10px; padding: 1em;
padding: 1em; background-color: var(--color-image-frame);
background-color: var(--color-image-frame);
figure {
margin: 0.5em;
}
}
figure { figure {
max-width: 100%; max-width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin: auto; margin: 0.5em;
picture { picture {
img { img {
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
&.rounded {
border-radius: 10px;
}
} }
} }
figcaption { figcaption {
padding-top: 0.75em; padding-top: 0.75em;
font-size: 1.8em; font-size: 1.8em;

@ -9,7 +9,7 @@
{#if imageData} {#if imageData}
<picture> <picture>
{#each imageData.formats as format} {#each imageData.formats as format}
<source media={`(min-width: ${format.width * 2}px)`} type={format.mime} srcset={format.url} /> <source media={format.width? `(min-width: ${format.width * 2}px)` : "(min-width: 0px)"} type={format.mime} srcset={format.url} />
{/each} {/each}
<img src={imageData.formats[0]?.url} alt={imageData.altText} /> <img src={imageData.formats[0]?.url} alt={imageData.altText} />
</picture> </picture>

@ -0,0 +1,42 @@
<script lang="ts">
import type { BlogPostContentEntry } from "$lib/cms/blog";
import "$lib/vars.scss";
import Box from "../atoms/Box.svelte";
import Markdown from "../atoms/Markdown.svelte";
import Image from "../molecules/Image.svelte";
export let content: BlogPostContentEntry;
</script>
<div class="post-content">
{#if content.__component === "content.text-markdown"}
{#if content.type === "paragraph"}
<Markdown markdown={content.value} />
{:else if content.type === "infobox"}
<Box color="cyan">
<b class="info-label">Info</b>
<Markdown markdown={content.value} />
</Box>
{/if}
{:else if content.__component === "content.image"}
{@const imageData = content.value.data.attributes}
<Image
imageData={{
caption: imageData.caption,
altText: imageData.alternativeText,
formats: Object.values(imageData.formats),
}}
/>
{/if}
</div>
<style lang="scss">
@layer component {
.post-content {
margin: 1em 0;
}
.info-label {
color: var(--color-cyan);
}
}
</style>

@ -25,10 +25,22 @@ export type BlogPostTeaser = {
export type BlogPost = BlogPostTeaser & { export type BlogPost = BlogPostTeaser & {
attributes: { attributes: {
content: any; content: BlogPostContentEntry[];
}; };
}; };
type BlogPostContentTemplate<S, T> = {
id: number;
__component: S;
} & T;
export type BlogPostContentEntry =
| BlogPostContentTemplate<
"content.text-markdown",
{ value: string; type: "paragraph" | "infobox" }
>
| BlogPostContentTemplate<"content.image", { value: { data: StrapiImage } }>;
export async function getPosts(locale = "all"): Promise<BlogPostTeaser[]> { export async function getPosts(locale = "all"): Promise<BlogPostTeaser[]> {
return await fetchApi<BlogPostTeaser[]>({ return await fetchApi<BlogPostTeaser[]>({
endpoint: "blog-posts", endpoint: "blog-posts",
@ -72,6 +84,9 @@ export async function getPost(slug: string): Promise<BlogPost> {
teaserImage: { teaserImage: {
populate: "*", populate: "*",
}, },
content: {
populate: "*",
},
}, },
filters: { filters: {
slug: { slug: {

@ -15,15 +15,15 @@ export type ImageMetadata = {
altText?: string; altText?: string;
formats: { formats: {
ext: string; ext?: string;
url: string; url: string;
hash: string; hash?: string;
mime: string; mime: string;
name: string; name: string;
path?: string; path?: string;
size: number; size?: number;
width: number; width?: number;
height: number; height?: number;
sizeInBytes: number; sizeInBytes?: number;
}[]; }[];
}; };

@ -40,7 +40,7 @@
} }
@include landscape { @include landscape {
margin: 0 30%; margin: 0 25%;
} }
} }

@ -1,7 +1,7 @@
<script> <script>
import "$lib/vars.scss"; import "$lib/vars.scss";
import Box from "../components/atoms/Box.svelte"; import Box from "../components/atoms/Box.svelte";
import Image from "../components/molecules/Image.svelte"; import Thumbnail from "../components/molecules/Thumbnail.svelte";
</script> </script>
<h1>Welcome to my website</h1> <h1>Welcome to my website</h1>
@ -9,21 +9,24 @@
<div class="flex-row text-and-image"> <div class="flex-row text-and-image">
<div class="text"> <div class="text">
<p> <p>
Heyyy. I'm a software developer and tinkerer from Heyyy. I'm a software developer and tinkerer from Germany. I do a lot of
Germany. I do a lot of stuff so this website is an attempt in providing stuff so this website is an attempt in providing an overview.
an overview.
</p> </p>
</div> </div>
<div class="image"> <div class="image">
<Image <Thumbnail
border={false} imageData={{
sources={[ altText:
{ "A picture of Ferris, the Rust mascot. An orange crab plushie.",
src: "/images/profile.jpg", formats: [
type: "image/jpeg", {
}, name: "original",
]} mime: "image/jpeg",
></Image> url: "/images/profile.jpg",
},
],
}}
/>
</div> </div>
</div> </div>
</Box> </Box>
@ -33,7 +36,7 @@
.text { .text {
display: box; display: box;
width: 63%; width: 63%;
margin-right: 3% margin-right: 3%;
} }
.image { .image {

@ -1,11 +1,27 @@
<script lang="ts"> <script lang="ts">
import Error from "../../../components/molecules/Error.svelte"; import Box from "../../../components/atoms/Box.svelte";
import Error from "../../../components/molecules/Error.svelte";
import BlogPostContent from "../../../components/organisms/BlogPostContent.svelte";
import type { PageData } from "./$types"; import type { PageData } from "./$types";
export let data: PageData; export let data: PageData;
</script> </script>
<code>{JSON.stringify(data.post, null, 2)}</code> {#if data.post}
<Box>
{@const post = data.post}
{@const author = data.post.attributes.author.data?.attributes}
<h1>{post.attributes.title}</h1>
<h3>by {author?.name}</h3>
{#each post.attributes.content as contentEntry}
<BlogPostContent content={contentEntry}/>
{/each}
</Box>
{:else}
<Error error={{message: "Could not find the post"}} />
{/if}
{#if data.error} {#if data.error}
<Error error={data.error} /> <Error error={data.error} />

Loading…
Cancel
Save