mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-12-11 09:27:30 +00:00
feat: add blogs support
This commit is contained in:
parent
eaf2d19541
commit
842f602b64
248
package-lock.json
generated
248
package-lock.json
generated
@ -1,248 +0,0 @@
|
||||
{
|
||||
"name": "spotube",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"svelte-media-queries": "^1.6.2",
|
||||
"svelte-persisted-store": "^0.9.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
|
||||
"integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
||||
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.0.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/trace-mapping": "^0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
|
||||
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/set-array": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
|
||||
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.22",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz",
|
||||
"integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/aria-query": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
|
||||
"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"dequal": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/axobject-query": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz",
|
||||
"integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"dequal": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/code-red": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz",
|
||||
"integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.4.15",
|
||||
"@types/estree": "^1.0.1",
|
||||
"acorn": "^8.10.0",
|
||||
"estree-walker": "^3.0.3",
|
||||
"periscopic": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/css-tree": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
|
||||
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"mdn-data": "2.0.30",
|
||||
"source-map-js": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dequal": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
||||
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/estree-walker": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
|
||||
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-reference": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz",
|
||||
"integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/locate-character": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
|
||||
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.7",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz",
|
||||
"integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.4.15"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/mdn-data": {
|
||||
"version": "2.0.30",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
|
||||
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/periscopic": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz",
|
||||
"integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0",
|
||||
"estree-walker": "^3.0.0",
|
||||
"is-reference": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte": {
|
||||
"version": "4.2.10",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.10.tgz",
|
||||
"integrity": "sha512-Ep06yCaCdgG1Mafb/Rx8sJ1QS3RW2I2BxGp2Ui9LBHSZ2/tO/aGLc5WqPjgiAP6KAnLJGaIr/zzwQlOo1b8MxA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.15",
|
||||
"@jridgewell/trace-mapping": "^0.3.18",
|
||||
"@types/estree": "^1.0.1",
|
||||
"acorn": "^8.9.0",
|
||||
"aria-query": "^5.3.0",
|
||||
"axobject-query": "^4.0.0",
|
||||
"code-red": "^1.0.3",
|
||||
"css-tree": "^2.3.1",
|
||||
"estree-walker": "^3.0.3",
|
||||
"is-reference": "^3.0.1",
|
||||
"locate-character": "^3.0.0",
|
||||
"magic-string": "^0.30.4",
|
||||
"periscopic": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-media-queries": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/svelte-media-queries/-/svelte-media-queries-1.6.2.tgz",
|
||||
"integrity": "sha512-SMz6od/vIeZEGlc4P0HKJK4G0fZotuwFhCSpBQaPqh75h6sL6sNf+4+IjbegFKXbP7b+SOfyzVOIMXTr8jynkA=="
|
||||
},
|
||||
"node_modules/svelte-persisted-store": {
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/svelte-persisted-store/-/svelte-persisted-store-0.9.1.tgz",
|
||||
"integrity": "sha512-l00I8Dy5GKjdjnE9ZcMeXLLMhvgV0+Iuru0Mue7eU3tB+pHBwBB2RVVqw2uC2Hbrf7cyZtsV/lnPKhjTHIWphQ==",
|
||||
"engines": {
|
||||
"node": ">=0.14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^3.48.0 || ^4.0.0 || ^5.0.0-next.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"svelte-media-queries": "^1.6.2",
|
||||
"svelte-persisted-store": "^0.9.1"
|
||||
}
|
||||
}
|
||||
1372
website/package-lock.json
generated
1372
website/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -49,9 +49,15 @@
|
||||
"date-fns": "^3.3.1",
|
||||
"highlight.js": "11.9.0",
|
||||
"lucide-svelte": "^0.323.0",
|
||||
"mdsvex-relative-images": "^1.0.3",
|
||||
"rehype-autolink-headings": "^7.1.0",
|
||||
"rehype-slug": "^6.0.0",
|
||||
"remark-container": "^0.1.2",
|
||||
"remark-external-links": "^9.0.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-github": "^12.0.0",
|
||||
"remark-reading-time": "^2.0.1",
|
||||
"svelte-fa": "^4.0.2",
|
||||
"svelte-markdown": "^0.4.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
0
website/posts/images/.gitkeep
Normal file
0
website/posts/images/.gitkeep
Normal file
41
website/posts/spotube-basics.md
Normal file
41
website/posts/spotube-basics.md
Normal file
@ -0,0 +1,41 @@
|
||||
---
|
||||
title: Spotube Basics
|
||||
author: Kingkor Roy Tirtho
|
||||
date: 2024-02-10
|
||||
published: true
|
||||
---
|
||||
|
||||
Spotube is an open-source Spotify client that allows users to stream music from Spotify. To use Spotube, you need to sign in with your Spotify account. Here's a step-by-step guide on how to sign in to Spotube.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, make sure you have the following:
|
||||
|
||||
- A Spotify account
|
||||
- The Spotube application installed on your device
|
||||
|
||||
## Steps
|
||||
|
||||
1. Open the Spotube application on your device.
|
||||
2. Click on the "Sign in with Spotify" button.
|
||||
3. You will be redirected to the Spotify login page. Enter your Spotify username and password and click on the "Log In" button.
|
||||
4. You will be asked to grant Spotube permission to access your Spotify account. Click on the "Agree" button to grant permission.
|
||||
5. You will be redirected back to the Spotube application, and you should now be signed in to your Spotify account.
|
||||
|
||||
That's it! You are now signed in to Spotube and can start streaming music from Spotify.
|
||||
|
||||
| Title | Author | Date | Published |
|
||||
| -------------- | ------------------ | ---------- | --------- |
|
||||
| Spotube Basics | Kingkor Roy Tirtho | 2024-02-10 | true |
|
||||
|
||||
```bash
|
||||
$ git clone
|
||||
```
|
||||
|
||||
```javascript
|
||||
const a = 1;
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
Signing in to Spotube is a simple process that requires you to have a Spotify account and the Spotube application installed on your device. By following the steps outlined in this tutorial, you should be able to sign in to Spotube and start streaming music from Spotify.
|
||||
@ -1,3 +0,0 @@
|
||||
<article class="prose lg:prose-lg max-w-3xl">
|
||||
<slot />
|
||||
</article>
|
||||
3
website/src/lib/components/markdown/layout.svelte
Normal file
3
website/src/lib/components/markdown/layout.svelte
Normal file
@ -0,0 +1,3 @@
|
||||
<article class="prose lg:prose-lg dark:prose-invert max-w-3xl">
|
||||
<slot />
|
||||
</article>
|
||||
41
website/src/lib/posts.ts
Normal file
41
website/src/lib/posts.ts
Normal file
@ -0,0 +1,41 @@
|
||||
export interface Post {
|
||||
date: string;
|
||||
title: string;
|
||||
tags: string[];
|
||||
published: boolean;
|
||||
author: string;
|
||||
readingTime: {
|
||||
text: string;
|
||||
minutes: number;
|
||||
time: number;
|
||||
words: number;
|
||||
};
|
||||
reading_time_text: string;
|
||||
preview_html: string;
|
||||
preview: string;
|
||||
previewHtml: string;
|
||||
slug: string | null;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export const getPosts = async () => {
|
||||
// Fetch posts from local Markdown files
|
||||
const posts: Post[] = await Promise.all(
|
||||
Object.entries(import.meta.glob('../../posts/**/*.md')).map(async ([path, resolver]) => {
|
||||
const resolved = (await resolver()) as { metadata: Post };
|
||||
const { metadata } = resolved;
|
||||
const slug = path.split('/').pop()?.slice(0, -3) ?? '';
|
||||
return { ...metadata, slug };
|
||||
})
|
||||
);
|
||||
|
||||
let sortedPosts = posts.sort((a, b) => +new Date(b.date) - +new Date(a.date));
|
||||
|
||||
sortedPosts = sortedPosts.map((post) => ({
|
||||
...post
|
||||
}));
|
||||
|
||||
return {
|
||||
posts: sortedPosts
|
||||
};
|
||||
};
|
||||
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import '../app.postcss';
|
||||
import Navbar from '../components/navbar/navbar.svelte';
|
||||
import Navbar from '$lib/components/navbar/navbar.svelte';
|
||||
|
||||
// Highlight JS
|
||||
import hljs from 'highlight.js/lib/core';
|
||||
@ -23,7 +23,7 @@
|
||||
storePopup.set({ computePosition, autoUpdate, flip, shift, offset, arrow });
|
||||
|
||||
import { initializeStores } from '@skeletonlabs/skeleton';
|
||||
import NavDrawer from '../components/navdrawer/navdrawer.svelte';
|
||||
import NavDrawer from '../lib/components/navdrawer/navdrawer.svelte';
|
||||
import Fa from 'svelte-fa';
|
||||
import { faGithub } from '@fortawesome/free-brands-svg-icons';
|
||||
initializeStores();
|
||||
|
||||
9
website/src/routes/api/posts/+server.ts
Normal file
9
website/src/routes/api/posts/+server.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { getPosts } from '$lib/posts';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { json } from '@sveltejs/kit';
|
||||
|
||||
export const GET: RequestHandler = async () => {
|
||||
const { posts } = await getPosts();
|
||||
|
||||
return json(posts);
|
||||
};
|
||||
@ -0,0 +1,32 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
const formatter = Intl.DateTimeFormat('en-US', {
|
||||
dateStyle: 'medium'
|
||||
});
|
||||
</script>
|
||||
|
||||
<section class="p-4 md:p-16 flex flex-col gap-4">
|
||||
<h2 class="h2">Blog Posts</h2>
|
||||
<br />
|
||||
<article class="grid sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4">
|
||||
{#each data.posts as post}
|
||||
<a
|
||||
href={`/blog/${post.slug}`}
|
||||
class="card hover:brightness-95 active:bg-secondary-hover-token active:scale-95 transition-all variant-ghost-secondary p-4"
|
||||
>
|
||||
<h4 class="h4">{post.title}</h4>
|
||||
<p>By {post.author}</p>
|
||||
<br />
|
||||
<p class="text-end">
|
||||
Published on
|
||||
<span class="font-medium underline decoration-dotted">
|
||||
{formatter.format(new Date(post.date))}
|
||||
</span>
|
||||
</p>
|
||||
</a>
|
||||
{/each}
|
||||
</article>
|
||||
</section>
|
||||
11
website/src/routes/blog/+page.ts
Normal file
11
website/src/routes/blog/+page.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import type { Post } from '$lib/posts.js';
|
||||
|
||||
export const load = async ({ fetch }) => {
|
||||
const res = await fetch(`api/posts`);
|
||||
if (res.ok) {
|
||||
const posts: Post[] = await res.json();
|
||||
return { posts };
|
||||
} else {
|
||||
return { posts: [] };
|
||||
}
|
||||
};
|
||||
26
website/src/routes/blog/[slug]/+page.svelte
Normal file
26
website/src/routes/blog/[slug]/+page.svelte
Normal file
@ -0,0 +1,26 @@
|
||||
<script lang="ts">
|
||||
import Layout from '$lib/components/markdown/layout.svelte';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
let {
|
||||
Content,
|
||||
meta: { date, title, readingTime }
|
||||
} = data as Required<PageData>;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Blog | {title}</title>
|
||||
</svelte:head>
|
||||
|
||||
<article class="p-4 md:p-16 flex flex-grow flex-col">
|
||||
<h1 class="h1">{title}</h1>
|
||||
<br />
|
||||
<div class="">
|
||||
<p>{new Date(date).toDateString()}</p>
|
||||
<p class="mb-16">{readingTime?.text ?? ''}</p>
|
||||
<Layout>
|
||||
<svelte:component this={Content} />
|
||||
</Layout>
|
||||
</div>
|
||||
</article>
|
||||
23
website/src/routes/blog/[slug]/+page.ts
Normal file
23
website/src/routes/blog/[slug]/+page.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import type { Post } from '$lib/posts.js';
|
||||
|
||||
export const load = async ({ params }) => {
|
||||
const { slug } = params;
|
||||
|
||||
try {
|
||||
const post = await import(`../../../../posts/${slug}.md`);
|
||||
return {
|
||||
Content: post.default as ConstructorOfATypedSvelteComponent,
|
||||
meta: {
|
||||
...post.metadata,
|
||||
slug,
|
||||
path: `/blog/${slug}`
|
||||
} as Post
|
||||
};
|
||||
} catch (err) {
|
||||
console.error('Error loading the post:', err);
|
||||
return {
|
||||
status: 500,
|
||||
error: `Could not load the post: ${(err as Error).message || err}`
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -2,7 +2,7 @@
|
||||
import { extendedDownloadLinks } from '$lib';
|
||||
import { Download } from 'lucide-svelte';
|
||||
import { History, Sparkles, Package } from 'lucide-svelte';
|
||||
import DownloadItems from '../../components/downloads/download-items.svelte';
|
||||
import DownloadItems from '$lib/components/downloads/download-items.svelte';
|
||||
|
||||
const otherDownloads: [string, string, any][] = [
|
||||
['/downloads/packages', 'CLI Packages Managers', Package],
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import { AlertTriangle, Bug, Sparkles } from 'lucide-svelte';
|
||||
import DownloadItems from '../../../components/downloads/download-items.svelte';
|
||||
import DownloadItems from '$lib/components/downloads/download-items.svelte';
|
||||
import { extendedNightlyDownloadLinks } from '$lib';
|
||||
</script>
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import SvelteMarkdown from 'svelte-markdown';
|
||||
import type { PageData } from './$types';
|
||||
import { formatDistanceToNow, formatRelative } from 'date-fns';
|
||||
import Layout from '../../../components/markdown/layout.svelte';
|
||||
import Layout from '$lib/components/markdown/layout.svelte';
|
||||
import { Accordion, AccordionItem } from '@skeletonlabs/skeleton';
|
||||
import { Book, History } from 'lucide-svelte';
|
||||
import {
|
||||
|
||||
@ -1,6 +1,12 @@
|
||||
import adapter from '@sveltejs/adapter-netlify';
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||
import { mdsvex } from 'mdsvex';
|
||||
import readingTime from 'remark-reading-time';
|
||||
import remarkExternalLinks from 'remark-external-links';
|
||||
import slugPlugin from 'rehype-slug';
|
||||
import autolinkHeadings from 'rehype-autolink-headings';
|
||||
import relativeImages from 'mdsvex-relative-images';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
@ -12,7 +18,28 @@ const config = {
|
||||
mdsvex({
|
||||
extensions: ['.svx', '.md'],
|
||||
highlight: {},
|
||||
layout: './src/components/markdown/layout.svelte'
|
||||
layout: './src/lib/components/markdown/layout.svelte',
|
||||
smartypants: {
|
||||
dashes: 'oldschool'
|
||||
},
|
||||
|
||||
remarkPlugins: [
|
||||
remarkGfm,
|
||||
// adds a `readingTime` frontmatter attribute
|
||||
readingTime(),
|
||||
relativeImages,
|
||||
// external links open in a new tab
|
||||
[remarkExternalLinks, { target: '_blank', rel: 'noopener' }]
|
||||
],
|
||||
rehypePlugins: [
|
||||
slugPlugin,
|
||||
[
|
||||
autolinkHeadings,
|
||||
{
|
||||
behavior: 'wrap'
|
||||
}
|
||||
]
|
||||
]
|
||||
})
|
||||
],
|
||||
vitePlugin: {
|
||||
|
||||
@ -3,11 +3,20 @@ import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit(), purgeCss({
|
||||
plugins: [
|
||||
sveltekit(),
|
||||
purgeCss({
|
||||
safelist: {
|
||||
// any selectors that begin with "hljs-" will not be purged
|
||||
greedy: [/^hljs-/],
|
||||
},
|
||||
}),
|
||||
greedy: [/^hljs-/]
|
||||
}
|
||||
})
|
||||
],
|
||||
});
|
||||
server: {
|
||||
fs: {
|
||||
// Allow serving files from one level up to the project root
|
||||
// posts, copy
|
||||
allow: ['..']
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user