This commit is contained in:
44
.gitea/workflows/build.yml
Normal file
44
.gitea/workflows/build.yml
Normal file
@@ -0,0 +1,44 @@
|
||||
name: Build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout project
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: https://github.com/pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
run_install: false
|
||||
|
||||
- name: Install node
|
||||
uses: https://github.com/actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: pnpm
|
||||
|
||||
- name: Install packages
|
||||
run: pnpm install
|
||||
|
||||
- name: Build project
|
||||
run: pnpm run build
|
||||
|
||||
- name: Check project
|
||||
run: pnpm run check
|
||||
|
||||
- name: Upload artifact
|
||||
run: |
|
||||
tar -czf build.tar.gz build
|
||||
curl --user minijack:${{ secrets.PACKAGE_TOKEN }} \
|
||||
--upload-file build.tar.gz \
|
||||
https://git.quartznet.info/api/packages/quartznet/generic/artifacts/ref-${{ gitea.sha }}/build-${{ gitea.sha }}.tar.gz
|
||||
echo https://git.quartznet.info/api/packages/quartznet/generic/artifacts/ref-${{ gitea.sha }}/build-${{ gitea.sha }}.tar.gz
|
||||
27
.gitea/workflows/deploy.yml
Normal file
27
.gitea/workflows/deploy.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Deploy
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
runs-on: selfhost
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: selfhost
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Get artifact
|
||||
run: |
|
||||
curl --user minijack:${{ secrets.PACKAGE_TOKEN }} \
|
||||
https://git.quartznet.info/api/packages/quartznet/generic/artifacts/ref-${{ gitea.sha }}/build-${{ gitea.sha }}.tar.gz > build.tar.gz
|
||||
echo https://git.quartznet.info/api/packages/quartznet/generic/artifacts/ref-${{ gitea.sha }}/build-${{ gitea.sha }}.tar.gz
|
||||
- name: Extract artifact
|
||||
run: |
|
||||
tar -xvzf build.tar.gz
|
||||
- run: ls -la build
|
||||
- run: ls -la /www/
|
||||
- run: ls -la /www/quartznet-info/
|
||||
- name: Clean Deploy Area
|
||||
run: rm -rf /www/quartznet-info/build
|
||||
- name: Deploy
|
||||
run: mv build /www/quartznet-info/
|
||||
4
.gitignore
vendored
Executable file
4
.gitignore
vendored
Executable file
@@ -0,0 +1,4 @@
|
||||
.content-collections
|
||||
.svelte-kit
|
||||
build
|
||||
node_modules
|
||||
7
README.md
Executable file
7
README.md
Executable file
@@ -0,0 +1,7 @@
|
||||
# rgebee-info
|
||||
|
||||
Personal website.
|
||||
|
||||
1. `pnpm install`
|
||||
|
||||
2. `pnpm dev`
|
||||
47
content-collections.ts
Executable file
47
content-collections.ts
Executable file
@@ -0,0 +1,47 @@
|
||||
import { defineCollection, defineConfig } from "@content-collections/core";
|
||||
import { z } from "zod";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkParse from 'remark-parse'
|
||||
import rehypePrettyCode from "rehype-pretty-code";
|
||||
import remarkRehype from 'remark-rehype'
|
||||
import rehypeStringify from 'rehype-stringify'
|
||||
import { unified } from 'unified'
|
||||
|
||||
const parser = unified()
|
||||
.use(remarkParse)
|
||||
.use(remarkGfm)
|
||||
.use(remarkRehype)
|
||||
.use(rehypeStringify)
|
||||
.use(rehypePrettyCode, {
|
||||
theme: "catppuccin-macchiato"
|
||||
});
|
||||
|
||||
const posts = defineCollection({
|
||||
name: "posts",
|
||||
directory: "src/content/blog",
|
||||
include: "**/*.md",
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
dateCreated: z.coerce.date(),
|
||||
dateUpdated: z.coerce.date().optional(),
|
||||
draft: z.boolean().optional(),
|
||||
html: z.string().optional(),
|
||||
slug: z.string().optional()
|
||||
}),
|
||||
transform: async (doc) => {
|
||||
if (doc.content) {
|
||||
const processed = await parser.process(doc.content);
|
||||
return {
|
||||
...doc,
|
||||
html: String(processed),
|
||||
slug: doc._meta.fileName.slice(0, -3),
|
||||
};
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
});
|
||||
|
||||
export default defineConfig({
|
||||
collections: [posts],
|
||||
});
|
||||
33
package.json
Executable file
33
package.json
Executable file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "rgebee-info",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@content-collections/core": "^0.11.1",
|
||||
"@content-collections/vite": "^0.2.7",
|
||||
"@shikijs/rehype": "^3.14.0",
|
||||
"@sveltejs/adapter-static": "^3.0.10",
|
||||
"@sveltejs/kit": "^2.48.3",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.1.1",
|
||||
"rehype-pretty-code": "^0.14.1",
|
||||
"rehype-stringify": "^10.0.1",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"remark-parse": "^11.0.0",
|
||||
"remark-rehype": "^11.1.2",
|
||||
"shiki": "^3.14.0",
|
||||
"svelte": "^5.43.0",
|
||||
"svelte-check": "^4.3.3",
|
||||
"svelte-preprocess": "^6.0.3",
|
||||
"typescript": "^5.9.3",
|
||||
"unified": "^11.0.5",
|
||||
"vite": "^7.1.12",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
2187
pnpm-lock.yaml
generated
Normal file
2187
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
14
src/app.html
Executable file
14
src/app.html
Executable file
@@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta name="description" content="Personal website" />
|
||||
<link rel="stylesheet" href="/global.css" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body>
|
||||
%sveltekit.body%
|
||||
</body>
|
||||
</html>
|
||||
13
src/app.ts
Normal file
13
src/app.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { allPosts } from 'content-collections';
|
||||
import type { Post } from 'content-collections';
|
||||
|
||||
export const SITE_AUTHOR = 'Rgebee';
|
||||
export const SITE_TITLE = SITE_AUTHOR;
|
||||
export const SITE_DESCRIPTION = 'Personal website';
|
||||
export const SITE_URL = 'https://rgebee.quartznet.info';
|
||||
|
||||
export async function getBlogCollection(): Promise<Post[]> {
|
||||
return (allPosts)
|
||||
.filter(post => import.meta.env.DEV || !post.draft)
|
||||
.sort((a, b) => b.dateCreated.valueOf() - a.dateCreated.valueOf());
|
||||
}
|
||||
30
src/content/blog/desktop-workflows.md
Executable file
30
src/content/blog/desktop-workflows.md
Executable file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
title: 'Desktop workflow thoughts'
|
||||
description: 'Lorem ipsum dolor sit amet'
|
||||
dateCreated: 'Oct 31 2025'
|
||||
draft: true
|
||||
---
|
||||
|
||||
Finding ways to be more efficient using my desktop. My goal was to quickly switch between windows and improve my
|
||||
understanding of what options I have for my workflow.
|
||||
|
||||
## Starting out
|
||||
|
||||
I started by trying a few small features in the KDE desktop environment. I was used to floating so before trying
|
||||
other options I wanted to see how efficient floating could get. The first thing I did was pin apps to the
|
||||
taskbar allowing me to use shortcuts to quickly switch to them. Then I also tried some settings to maximise
|
||||
windows by default. This sort of worked though had some issues(popups, not applying to certain windows).
|
||||
|
||||
## Improving things
|
||||
|
||||
Next I started using workspaces, took some time to adjust but I found it helped manage windows into tasks. I settled
|
||||
on 3 main workspaces. Initially I kept using pinned apps but after a while switched to workspace shortcuts. This allowed me to easily change window focus on a workspace. For example browser on workspace 1, switch to file explorer
|
||||
on workspace 1 and now meta+1 will show that instead.
|
||||
|
||||
## Tiling
|
||||
|
||||
The next part you see many videos and discussion about is tiling. For me I had 3 workspaces so flat tiling didn't work
|
||||
for me since I couldn't have more than 1 maximised window per workspace and managing more workspaces added friction
|
||||
I didn't want.
|
||||
|
||||
Though that is only flat tiling(no overlapping windows). That leaves 2 other options, tabbing and scrolling.
|
||||
8
src/content/blog/overthinking-blog-creation.md
Executable file
8
src/content/blog/overthinking-blog-creation.md
Executable file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
title: 'Overthinking blog creation'
|
||||
description: 'Lorem ipsum dolor sit amet'
|
||||
dateCreated: 'Oct 31 2025'
|
||||
draft: true
|
||||
---
|
||||
|
||||
Testing
|
||||
87
src/content/blog/post-content-styles.md
Executable file
87
src/content/blog/post-content-styles.md
Executable file
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: 'Testing post content styles'
|
||||
description: 'Lorem ipsum dolor sit amet'
|
||||
dateCreated: 'Oct 28 2025'
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce bibendum neque eget
|
||||
nunc mattis eu sollicitudin enim tincidunt. Vestibulum lacus tortor, ultricies id
|
||||
dignissim ac, bibendum in velit.
|
||||
|
||||
# Test subheading (h1)
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||
## Test subheading (h2)
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||
### Test subheading (h3)
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||
#### Test subheading (h4)
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||
##### Test subheading (h5)
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||
## Test unordered list
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||
* Bullet point in the list
|
||||
* Bullet point in the list
|
||||
* Bullet point in the list
|
||||
* Bullet point in the list
|
||||
* Bullet point in the list
|
||||
|
||||
## Test ordered list
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||
1. First item in the list
|
||||
2. Second item in the list
|
||||
3. Third item in the list
|
||||
4. Fourth item in the list
|
||||
|
||||
## Test code
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<title>Test title</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Hello, world!</p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Test image
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||

|
||||
|
||||
## Test blockquote
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||
> Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
> Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||
## Test table
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
|
||||
| Title 1 | Title 2 |
|
||||
| --------------------- | --------------------- |
|
||||
| lorem ipsum dolor sit | lorem ipsum dolor sit |
|
||||
| lorem ipsum dolor sit | lorem ipsum dolor sit |
|
||||
| lorem ipsum dolor sit | lorem ipsum dolor sit |
|
||||
| lorem ipsum dolor sit | lorem ipsum dolor sit |
|
||||
5
src/routes/+error.svelte
Normal file
5
src/routes/+error.svelte
Normal file
@@ -0,0 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/state';
|
||||
</script>
|
||||
|
||||
<h1>{page.status} {page?.error?.message}</h1>
|
||||
71
src/routes/+layout.svelte
Executable file
71
src/routes/+layout.svelte
Executable file
@@ -0,0 +1,71 @@
|
||||
<script>
|
||||
import { SITE_URL, SITE_TITLE } from '../app';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<link
|
||||
rel="alternate"
|
||||
type="application/atom+xml"
|
||||
title={SITE_TITLE}
|
||||
href={new URL("feed.atom", SITE_URL).toString()}
|
||||
/>
|
||||
</svelte:head>
|
||||
|
||||
<header class="container">
|
||||
<nav>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/">Home</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/blog">Blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container">
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<footer class="container">
|
||||
<a href="/feed.atom" target="_blank" rel="noopener">RSS</a>
|
||||
</footer>
|
||||
|
||||
<style>
|
||||
header {
|
||||
color: var(--text-color);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
nav {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
gap: 25px;
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
nav li {
|
||||
font-weight: bold;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
border-top: 3px solid var(--border1-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
</style>
|
||||
2
src/routes/+layout.ts
Executable file
2
src/routes/+layout.ts
Executable file
@@ -0,0 +1,2 @@
|
||||
export const prerender = true;
|
||||
export const trailingSlash = 'always';
|
||||
78
src/routes/+page.svelte
Executable file
78
src/routes/+page.svelte
Executable file
@@ -0,0 +1,78 @@
|
||||
<script lang="ts">
|
||||
import type { PageProps } from './$types';
|
||||
import { SITE_TITLE } from '../app';
|
||||
|
||||
let { data }: PageProps = $props();
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{SITE_TITLE}</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="title">
|
||||
<img src="/images/logo.png" alt="logo" />
|
||||
<h1>{SITE_TITLE}</h1>
|
||||
</div>
|
||||
<p>Welcome to my website. I'm a software developer interested in game development and design.</p>
|
||||
|
||||
<div class="posts">
|
||||
<h2>Latest posts</h2>
|
||||
<ul class="post-list">
|
||||
{#each data.posts as post}
|
||||
<li class="post">
|
||||
<div>
|
||||
<a href="/blog/{post.slug}">{post.title}</a>
|
||||
</div>
|
||||
<span class="post-meta">{post.dateCreated.toISOString().split("T")[0]}</span>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<a class="blog-link" href="/blog">All posts →</a>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.title h1 {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.title img {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.post-list {
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.post {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 10px 0px;
|
||||
}
|
||||
|
||||
.post a {
|
||||
font-size: 1.25rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.post-meta {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.blog-link {
|
||||
font-size: 1rem;
|
||||
}
|
||||
</style>
|
||||
10
src/routes/+page.ts
Executable file
10
src/routes/+page.ts
Executable file
@@ -0,0 +1,10 @@
|
||||
import type { PageLoad } from './$types';
|
||||
import { getBlogCollection } from '../app'
|
||||
|
||||
export const load: PageLoad = async ( fetch ) => {
|
||||
let posts = (await getBlogCollection()).slice(0, 5);
|
||||
|
||||
return {
|
||||
posts,
|
||||
};
|
||||
};
|
||||
58
src/routes/blog/+page.svelte
Executable file
58
src/routes/blog/+page.svelte
Executable file
@@ -0,0 +1,58 @@
|
||||
<script lang="ts">
|
||||
import type { PageProps } from './$types';
|
||||
import { SITE_TITLE } from '../../app';
|
||||
|
||||
let { data }: PageProps = $props();
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Blog - {SITE_TITLE}</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="title">
|
||||
<h1>Blog</h1>
|
||||
</div>
|
||||
|
||||
<div class="posts">
|
||||
<ul class="post-list">
|
||||
{#each data.posts as post}
|
||||
<li class="post">
|
||||
<div>
|
||||
<a href="/blog/{post.slug}">{post.title}</a>
|
||||
</div>
|
||||
<span class="post-meta">{post.dateCreated.toISOString().split("T")[0]}</span>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.title h1 {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.post-list {
|
||||
padding-top: 0;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.post {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 10px 0px;
|
||||
}
|
||||
|
||||
.post a {
|
||||
font-size: 1.25rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.post-meta {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
</style>
|
||||
9
src/routes/blog/+page.ts
Executable file
9
src/routes/blog/+page.ts
Executable file
@@ -0,0 +1,9 @@
|
||||
import type { PageLoad } from './$types';
|
||||
import { getBlogCollection } from '../../app';
|
||||
|
||||
export const load: PageLoad = async () => {
|
||||
let posts = await getBlogCollection();
|
||||
return {
|
||||
posts,
|
||||
};
|
||||
};
|
||||
40
src/routes/blog/[slug]/+page.svelte
Executable file
40
src/routes/blog/[slug]/+page.svelte
Executable file
@@ -0,0 +1,40 @@
|
||||
<script lang="ts">
|
||||
import type { PageProps } from './$types';
|
||||
|
||||
let { data }: PageProps = $props();
|
||||
|
||||
const post = data.post;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{post.title}</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="title">
|
||||
<h1>{post.title}</h1>
|
||||
<span>{post.dateCreated.toISOString().split("T")[0]}</span>
|
||||
</div>
|
||||
|
||||
{@html post.html}
|
||||
|
||||
<style>
|
||||
.title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
border-bottom: 3px solid var(--border1-color);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.title h1 {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.title span {
|
||||
margin-bottom: 10px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: normal;
|
||||
color: var(--text-color);
|
||||
}
|
||||
</style>
|
||||
14
src/routes/blog/[slug]/+page.ts
Executable file
14
src/routes/blog/[slug]/+page.ts
Executable file
@@ -0,0 +1,14 @@
|
||||
import type { PageLoad } from './$types';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { getBlogCollection } from '../../../app';
|
||||
|
||||
export const load: PageLoad = async ({ params }) => {
|
||||
const post = (await getBlogCollection()).find((post) => post.slug == params.slug);
|
||||
if (!post) {
|
||||
error(404, `Could not find ${params.slug}`);
|
||||
}
|
||||
|
||||
return {
|
||||
post: post
|
||||
};
|
||||
};
|
||||
72
src/routes/feed.atom/+server.ts
Executable file
72
src/routes/feed.atom/+server.ts
Executable file
@@ -0,0 +1,72 @@
|
||||
import { getBlogCollection } from '../../app';
|
||||
import { SITE_AUTHOR, SITE_TITLE, SITE_DESCRIPTION, SITE_URL } from '../../app';
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
function escapeXml(str: string): string {
|
||||
return str
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
const posts = await getBlogCollection();
|
||||
|
||||
const dateUpdated = posts.length
|
||||
? new Date(
|
||||
Math.max(
|
||||
...posts.map(post => {
|
||||
const updated = post.dateUpdated ? post.dateUpdated.getTime() : 0;
|
||||
const created = post.dateCreated ? post.dateCreated.getTime() : 0;
|
||||
return Math.max(updated, created);
|
||||
})
|
||||
)
|
||||
)
|
||||
: null;
|
||||
|
||||
// Start Atom feed
|
||||
let atomXml = `<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<id>${SITE_URL}/</id>
|
||||
<title>${escapeXml(SITE_TITLE)}</title>
|
||||
<subtitle>${escapeXml(SITE_DESCRIPTION)}</subtitle>
|
||||
<link href="${SITE_URL}/feed.atom" rel="self"/>
|
||||
<link href="${SITE_URL}"/>
|
||||
<updated>${dateUpdated?.toISOString()}</updated>
|
||||
<author>
|
||||
<name>${SITE_AUTHOR}</name>
|
||||
</author>`;
|
||||
|
||||
posts.forEach((post) => {
|
||||
const url = `${SITE_URL}/blog/${post.slug}`;
|
||||
const dateCreated = post.dateCreated;
|
||||
const dateUpdated = post.dateUpdated ?? post.dateCreated;
|
||||
|
||||
const htmlContent = post.html ?? '';
|
||||
|
||||
atomXml += `
|
||||
<entry>
|
||||
<id>${url}</id>
|
||||
<title>${post.title}</title>
|
||||
<link href="${url}"/>
|
||||
<published>${dateCreated?.toISOString()}</published>
|
||||
<updated>${dateUpdated?.toISOString()}</updated>
|
||||
<author>
|
||||
<name>${SITE_AUTHOR}</name>
|
||||
</author>
|
||||
<content type="html">
|
||||
${escapeXml(htmlContent)}
|
||||
</content>
|
||||
<summary>${post.description || ''}</summary>
|
||||
</entry>`;
|
||||
});
|
||||
|
||||
atomXml += '\n</feed>';
|
||||
|
||||
return new Response(atomXml, {
|
||||
headers: { 'Content-Type': 'application/atom+xml' }
|
||||
});
|
||||
}
|
||||
48
static/favicon.svg
Normal file
48
static/favicon.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 23 KiB |
175
static/global.css
Executable file
175
static/global.css
Executable file
@@ -0,0 +1,175 @@
|
||||
:root {
|
||||
--background-color: #212529;
|
||||
--text-color: #ededed;
|
||||
--primary-color: #133b6e;
|
||||
--accent-color: #dc7c28;
|
||||
--link-color: #569cd6;
|
||||
--border1-color: #dc7c28;
|
||||
--border2-color: #3d444d;
|
||||
}
|
||||
|
||||
body {
|
||||
box-sizing: border-box;
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
font-family: sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
html {
|
||||
min-height: 100%;
|
||||
scroll-behavior: smooth;
|
||||
font-size: 100%;
|
||||
} /* 16px */
|
||||
|
||||
h1 {
|
||||
font-size: 1.802rem; /* 28.8px */
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.602rem; /* 25.6px */
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.424rem; /* 22.72px */
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.266rem; /* 20.32px */
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.125rem; /* 18.08px */
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 0.889rem; /* 14.24px */
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 10px;
|
||||
color: var(--text-color);
|
||||
line-height: 1.2;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
list-style-position: inside;
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
ul ul {
|
||||
list-style-type: disc;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
table {
|
||||
margin-bottom: 1rem;
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-color: inherit;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.container {
|
||||
width: 540px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.container {
|
||||
width: 720px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.container {
|
||||
width: 960px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.container {
|
||||
width: 1140px;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
background-color: var(--border1-color);
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 4px solid var(--border1-color);
|
||||
margin-left: 0px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
a {
|
||||
border-bottom: 2px solid transparent;
|
||||
color: var(--link-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
border-bottom: 2px solid var(--link-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
main {
|
||||
color: var(--text-color);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
main img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Code highlighting */
|
||||
|
||||
figure {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 8px;
|
||||
border: 1px solid var(--border2-color);
|
||||
}
|
||||
|
||||
pre code {
|
||||
overflow-x: scroll;
|
||||
display: block;
|
||||
border-radius: 0;
|
||||
color: #e1e4e8;
|
||||
}
|
||||
BIN
static/images/computer-problems.png
Normal file
BIN
static/images/computer-problems.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
BIN
static/images/logo.png
Executable file
BIN
static/images/logo.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
17
svelte.config.js
Executable file
17
svelte.config.js
Executable file
@@ -0,0 +1,17 @@
|
||||
import adapter from "@sveltejs/adapter-static";
|
||||
import sveltePreprocess from "svelte-preprocess";
|
||||
import path from 'path';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
kit: {
|
||||
adapter: adapter(),
|
||||
alias: {
|
||||
"content-collections": "./.content-collections/generated",
|
||||
}
|
||||
},
|
||||
extensions: [".svelte", ".md"],
|
||||
preprocess: [sveltePreprocess()],
|
||||
};
|
||||
|
||||
export default config;
|
||||
19
tsconfig.json
Normal file
19
tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "./.svelte-kit/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "bundler"
|
||||
}
|
||||
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
|
||||
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
|
||||
//
|
||||
// To make changes to top-level options such as include and exclude, we recommend extending
|
||||
// the generated config; see https://svelte.dev/docs/kit/configuration#typescript
|
||||
}
|
||||
8
vite.config.ts
Executable file
8
vite.config.ts
Executable file
@@ -0,0 +1,8 @@
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import contentCollections from "@content-collections/vite";
|
||||
|
||||
const config = {
|
||||
plugins: [sveltekit(), contentCollections()]
|
||||
};
|
||||
|
||||
export default config;
|
||||
Reference in New Issue
Block a user