Add furhter processing to posts display page

This commit is contained in:
Fred Boniface 2023-08-18 21:47:52 +01:00
parent d761f72248
commit bff88838d3
13 changed files with 334 additions and 86 deletions

146
package-lock.json generated
View File

@ -8,13 +8,17 @@
"name": "fredboniface.co.uk-svelte", "name": "fredboniface.co.uk-svelte",
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
"mongodb": "^5.7.0" "marked": "^7.0.3",
"mongodb": "^5.7.0",
"sanitize-html": "^2.11.0"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.28.1", "@playwright/test": "^1.28.1",
"@sveltejs/adapter-auto": "^2.0.0", "@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/adapter-node": "^1.3.1", "@sveltejs/adapter-node": "^1.3.1",
"@sveltejs/kit": "^1.20.4", "@sveltejs/kit": "^1.20.4",
"@types/node": "^20.5.1",
"@types/sanitize-html": "^2.9.0",
"@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0", "@typescript-eslint/parser": "^5.45.0",
"eslint": "^8.28.0", "eslint": "^8.28.0",
@ -916,9 +920,9 @@
"dev": true "dev": true
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.5.0", "version": "20.5.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.0.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz",
"integrity": "sha512-Mgq7eCtoTjT89FqNoTzzXg2XvCi5VMhRV6+I2aYanc6kQCBImeNaAYRs/DyoVqk1YEUJK5gN9VO7HRIdz4Wo3Q==" "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg=="
}, },
"node_modules/@types/pug": { "node_modules/@types/pug": {
"version": "2.0.6", "version": "2.0.6",
@ -932,6 +936,15 @@
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
"dev": true "dev": true
}, },
"node_modules/@types/sanitize-html": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.9.0.tgz",
"integrity": "sha512-4fP/kEcKNj2u39IzrxWYuf/FnCCwwQCpif6wwY6ROUS1EPRIfWJjGkY3HIowY1EX/VbX5e86yq8AAE7UPMgATg==",
"dev": true,
"dependencies": {
"htmlparser2": "^8.0.0"
}
},
"node_modules/@types/semver": { "node_modules/@types/semver": {
"version": "7.5.0", "version": "7.5.0",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz",
@ -1674,7 +1687,6 @@
"version": "4.3.1", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -1736,6 +1748,68 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.2",
"entities": "^4.2.0"
},
"funding": {
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/domelementtype": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
]
},
"node_modules/domhandler": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
"dependencies": {
"domelementtype": "^2.3.0"
},
"engines": {
"node": ">= 4"
},
"funding": {
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/domutils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
"dependencies": {
"dom-serializer": "^2.0.0",
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3"
},
"funding": {
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/es6-promise": { "node_modules/es6-promise": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
@ -1783,7 +1857,6 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },
@ -2282,6 +2355,24 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/htmlparser2": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
"domutils": "^3.0.1",
"entities": "^4.4.0"
}
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.2.4", "version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@ -2431,6 +2522,14 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/is-plain-object": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-reference": { "node_modules/is-reference": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.1.tgz", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.1.tgz",
@ -2585,6 +2684,17 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/marked": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/marked/-/marked-7.0.3.tgz",
"integrity": "sha512-ev2uM40p0zQ/GbvqotfKcSWEa59fJwluGZj5dcaUOwDRrB1F3dncdXy8NWUApk4fi8atU3kTBOwjyjZ0ud0dxw==",
"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",
@ -2777,7 +2887,6 @@
"version": "3.3.6", "version": "3.3.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@ -2880,6 +2989,11 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/parse-srcset": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz",
"integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q=="
},
"node_modules/path-exists": { "node_modules/path-exists": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@ -2951,8 +3065,7 @@
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
"dev": true
}, },
"node_modules/picomatch": { "node_modules/picomatch": {
"version": "2.3.1", "version": "2.3.1",
@ -2993,7 +3106,6 @@
"version": "8.4.28", "version": "8.4.28",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.28.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.28.tgz",
"integrity": "sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw==", "integrity": "sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -3344,6 +3456,19 @@
"rimraf": "bin.js" "rimraf": "bin.js"
} }
}, },
"node_modules/sanitize-html": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.11.0.tgz",
"integrity": "sha512-BG68EDHRaGKqlsNjJ2xUB7gpInPA8gVx/mvjO743hZaeMCZ2DwzW7xvsqZ+KNU4QKwj86HJ3uu2liISf2qBBUA==",
"dependencies": {
"deepmerge": "^4.2.2",
"escape-string-regexp": "^4.0.0",
"htmlparser2": "^8.0.0",
"is-plain-object": "^5.0.0",
"parse-srcset": "^1.0.2",
"postcss": "^8.3.11"
}
},
"node_modules/saslprep": { "node_modules/saslprep": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
@ -3468,7 +3593,6 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }

View File

@ -19,6 +19,8 @@
"@sveltejs/adapter-auto": "^2.0.0", "@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/adapter-node": "^1.3.1", "@sveltejs/adapter-node": "^1.3.1",
"@sveltejs/kit": "^1.20.4", "@sveltejs/kit": "^1.20.4",
"@types/node": "^20.5.1",
"@types/sanitize-html": "^2.9.0",
"@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0", "@typescript-eslint/parser": "^5.45.0",
"eslint": "^8.28.0", "eslint": "^8.28.0",
@ -36,6 +38,8 @@
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"mongodb": "^5.7.0" "marked": "^7.0.3",
"mongodb": "^5.7.0",
"sanitize-html": "^2.11.0"
} }
} }

View File

@ -6,7 +6,7 @@ let dbClient: MongoClient | null = null;
export async function mongoConnect() { export async function mongoConnect() {
if (!dbClient) { if (!dbClient) {
dbClient = await MongoClient.connect(MongoUri) dbClient = await MongoClient.connect(MongoUri);
} }
return dbClient return dbClient;
} }

View File

@ -1,17 +1,17 @@
<script lang="ts"> <script lang="ts">
import type { ArticleSummary } from "./types"; import type { ArticleSummary } from './types';
import FullWidthContent from "$lib/content-boxes/FullWidthContent.svelte"; import FullWidthContent from '$lib/content-boxes/FullWidthContent.svelte';
export let article: ArticleSummary export let article: ArticleSummary;
</script> </script>
<FullWidthContent> <FullWidthContent>
<a href="/posts/{article.slug}"> <a href="/posts/{article.slug}">
<article> <article>
<h1>{article.title}</h1> <h1>{article.title}</h1>
<p>{article.summary}</p> <p>{article.summary}</p>
</article> </article>
</a> </a>
</FullWidthContent> </FullWidthContent>
<style> <style>

View File

@ -1,20 +1,20 @@
export interface ArticleSummary { export interface ArticleSummary {
title: string; title: string;
author: string; author: string;
tags: string[]; tags: string[];
pusblished: boolean; pusblished: boolean;
date: Date; date: Date;
summary: string; summary: string;
slug: string; slug: string;
} }
export interface Article { export interface Article {
title: string; title: string;
author: string; author: string;
tags: string[]; tags: string[];
pusblished: boolean; pusblished: boolean;
date: Date; date: Date;
summary: string; summary: string;
slug: string; slug: string;
content: string; content: string;
} }

View File

@ -1,13 +1,13 @@
<script lang="ts"> <script lang="ts">
import type { Project } from "./types"; import type { Project } from './types';
import Logos from "$lib/language-logos/Logos.svelte"; import Logos from '$lib/language-logos/Logos.svelte';
export let project: Project export let project: Project;
</script> </script>
<article> <article>
<a href="/posts/tag/{project.tag}"><h1>{project.name}</h1></a> <a href="/posts/tag/{project.tag}"><h1>{project.name}</h1></a>
<Logos langs={project.lang || []} plats={project.plat || []} /> <Logos langs={project.lang || []} plats={project.plat || []} />
<p>{project.summary}</p> <p>{project.summary}</p>
</article> </article>
@ -27,4 +27,4 @@
margin-left: 10px; margin-left: 10px;
margin-right: 10px; margin-right: 10px;
} }
</style> </style>

View File

@ -35,15 +35,12 @@
<section id="meta-links"> <section id="meta-links">
<div id="projects" class="col"> <div id="projects" class="col">
<h1>Projects</h1> <h1>Projects</h1>
</div> </div>
<div id="posts" class="col"> <div id="posts" class="col">
<h1>Posts</h1> <h1>Posts</h1>
</div> </div>
<div id="tags" class="col"> <div id="tags" class="col">
<h1>Tags</h1> <h1>Tags</h1>
</div> </div>
</section> </section>

View File

@ -1,16 +1,57 @@
import { mongoConnect } from "$lib/database/mongo"; import { mongoConnect } from '$lib/database/mongo';
import { marked } from 'marked';
import * as path from 'path';
import sanitizeHtml from 'sanitize-html';
import type { Article } from '$lib/posts/types';
import type { _Renderer } from 'Renderer';
export async function load({ params }) { export async function load({ params }) {
const slug = params.slug; const slug = params.slug;
const db = await mongoConnect() const db = await mongoConnect();
const query = { const query = {
slug: slug slug: slug
} };
const col = db.db('fredboniface').collection('posts') const col = db.db('fredboniface').collection('posts');
const res = col.findOne(query) const res = await col.findOne(query);
const posts = await res
// Create the `Article` object here and return that rather than doing it on the page. if (!res) {
throw new Error('Post Not Found');
}
return {data: JSON.stringify(posts)} const renderer = new marked.Renderer();
renderer.image = function (href, title, text) {
const imgPath = path.join('/images/posts', res.slug, href);
return `<figure>
<img class="post-inline-image-546543" src="${imgPath}" alt="${text}" title="${title}">
<figcaption>${title}</figcaption>
</figure>`;
};
const post: Article = {
title: res.title,
author: res.author,
pusblished: res.published,
summary: res.summary,
content: renderMarkdown(res.content, renderer),
slug: res.slug,
tags: res.tags,
date: res.date
};
return { data: post };
}
function renderMarkdown(md: string, renderer: _Renderer): string {
const rawHtml = marked(md, { renderer });
console.log(rawHtml)
const sanitizedHtml = sanitizeHtml(rawHtml, {
allowedTags: ['a', 'br', 'code', 'figcaption', 'figure', 'img', 'p', 'pre'],
allowedAttributes: {
'a': ['href'],
'img': ['alt', 'class', 'src', 'title']
}
});
console.log(sanitizedHtml)
return sanitizedHtml
} }

View File

@ -1,24 +1,109 @@
<script lang="ts"> <script lang="ts">
import Header from "$lib/header.svelte"; import Header from '$lib/header.svelte';
import type { Article } from "$lib/posts/types"; import type { Article } from '$lib/posts/types';
import { afterUpdate } from 'svelte';
export let data: any; export let data: any;
const post: Article = JSON.parse(data.data) const post: Article = data.data;
afterUpdate(() => {
const codeBlocks = document.querySelectorAll('article pre');
codeBlocks.forEach((codeBlock) => {
const copyButton = document.createElement('button');
copyButton.textContent = '📋 Copy code';
copyButton.classList.add('article-code-copy-button-32984456');
copyButton.addEventListener('click', () => {
const codeText = codeBlock.textContent || '';
const trimmedCodeText = codeText.replace(copyButton.textContent || '', '');
navigator.clipboard
.writeText(trimmedCodeText)
.then(() => {
console.log('Copied code to clipboard');
})
.catch((error) => {
console.error(
'Error copying code to clipboard, this may be due to privacy settings in your browser'
);
});
});
codeBlock.appendChild(copyButton);
});
});
</script> </script>
<article>
<Header title={post.title} /> <Header title={post.title} />
<p>{#each post.tags as tag}
<span>{tag}</span> <div class="column-container">
{/each}<br> <div class="main-column">
{post.date}<br> <article>
{post.author}<br> <p>
{post.content}</p> {#each post.tags as tag}
<span>{tag}</span>
{/each}<br />
{post.date.toLocaleDateString()}<br />
Written by: {post.author}
</p>
<content>
{@html post.content}
</content>
</article> </article>
</div>
<div class="side-column">
<p>HELLO FROM COLUMN 2</p>
</div>
</div>
<style> <style>
.column-container {
display: flex;
flex-direction: column;
gap: 20px;
}
@media (min-width: 768px) {
.column-container {
flex-direction: row; /* Switch to row layout for larger screens */
}
.main-column {
flex: 2; /* Take up 2/3 of the available width */
}
.side-column {
flex: 1; /* Take up 1/3 of the available width */
}
}
span { span {
padding: 5px; padding: 5px;
} }
:global(article pre) {
position: relative;
text-align: left;
background-color: rgb(46, 46, 46);
color: white;
padding: 15px;
padding-top: 30px;
padding-bottom: 30px;
font-family: 'Courier New', Courier, monospace;
border-radius: 8px;
width: 90%;
max-width: 800px;
margin: auto;
white-space: pre;
overflow: auto;
}
:global(.article-code-copy-button-32984456) {
position: absolute;
top: 10px;
right: 15px;
color: white;
background: none;
border: none;
cursor: pointer;
}
</style> </style>

View File

@ -1,19 +1,16 @@
import { mongoConnect } from "$lib/database/mongo"; import { mongoConnect } from '$lib/database/mongo';
export async function load({ params }) { export async function load({ params }) {
const tag = params.tag; const tag = params.tag;
const db = await mongoConnect() const db = await mongoConnect();
const query = { const query = {
tags: { tags: {
$in: [ $in: [tag]
tag
]
}
} }
const col = db.db('fredboniface').collection('posts') };
const res = col.find(query) const col = db.db('fredboniface').collection('posts');
const posts = await res.toArray() const res = col.find(query);
const posts = await res.toArray();
return {data: JSON.stringify(posts)} return { data: JSON.stringify(posts) };
} }

View File

@ -1,15 +1,15 @@
<script lang="ts"> <script lang="ts">
import PostsSummary from "$lib/posts/PostsSummary.svelte"; import PostsSummary from '$lib/posts/PostsSummary.svelte';
import type { ArticleSummary } from "$lib/posts/types.js"; import type { ArticleSummary } from '$lib/posts/types.js';
export let data; export let data;
const posts: ArticleSummary[] = JSON.parse(data.data) const posts: ArticleSummary[] = JSON.parse(data.data);
console.log("Posts:", posts) console.log('Posts:', posts);
</script> </script>
<h1>Testing</h1> <h1>Testing</h1>
{#each posts as article} {#each posts as article}
<PostsSummary {article} /> <PostsSummary {article} />
{/each} {/each}

View File

@ -19,14 +19,14 @@
/> />
<p> <p>
Working full time on the 'iron road', left me wanting a faster way to get the information I Working full time on the 'iron road', left me wanting a faster way to get the information I
needed. OwlBoard evolved from <a href="/articles/athena">Athena</a> and grew to provide more needed. OwlBoard evolved from <a href="/articles/athena">Athena</a> and grew to provide more information
information that frontline rail colleagues need. that frontline rail colleagues need.
</p> </p>
</FullWidthContent> </FullWidthContent>
<FullWidthContent> <FullWidthContent>
<h1><a href="/posts/tag/map-dots">map-dots</a></h1> <h1><a href="/posts/tag/map-dots">map-dots</a></h1>
<br /> <br />
<GitLink url={"https://git.fjla.uk/fred.boniface/map-dots"} /> <GitLink url={'https://git.fjla.uk/fred.boniface/map-dots'} />
<Logos langs={['go', 'py']} plats={[]} /> <Logos langs={['go', 'py']} plats={[]} />
<p> <p>
I like to collect data, I am just not always sure what to do with that data. map-dots takes in I like to collect data, I am just not always sure what to do with that data. map-dots takes in

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB