Compare commits

..

1 Commits

11 changed files with 6022 additions and 9768 deletions

View File

@ -7,10 +7,10 @@ A blog framework
To create the production version, To create the production version,
```bash ```bash
pnpm run build npm run build
``` ```
You can preview the production build with `pnpm run dev --open`. You can preview the production build with `npm run preview`.
### Content ### Content
@ -35,23 +35,24 @@ image.
Install [pre-commit](https://pre-commit.com/). Install [pre-commit](https://pre-commit.com/).
Install dependencies with `pnpm install`. Then start the dev server with Install dependencies with `npm install` (or `pnpm install` or `yarn`). Then
start the dev server with
```bash ```bash
pnpm run dev [-- --open] npm run dev [-- --open]
``` ```
### Formatting ### Formatting
``` bash ``` bash
pnpx eslint npx eslint
``` ```
### Testing ### Testing
To run tests To run tests
``` bash ``` bash
pnpx playwright test npx playwright test
``` ```
All tests must have the suffix `.spec.[jt]s`. Be cautious while updating All tests must have the suffix `.spec.[jt]s`. Be cautious while updating

5927
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -16,47 +16,47 @@
"prepare": "svelte-kit sync" "prepare": "svelte-kit sync"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.29.0", "@eslint/js": "^9.11.1",
"@playwright/test": "^1.53.1", "@playwright/test": "^1.40.0",
"@sveltejs/adapter-auto": "^3.3.1", "@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-static": "^3.0.8", "@sveltejs/adapter-static": "^3.0.5",
"@sveltejs/kit": "^2.22.0", "@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^5.1.0", "@sveltejs/vite-plugin-svelte": "^3.0.0",
"@tailwindcss/typography": "^0.5.16", "@tailwindcss/typography": "^0.5.15",
"@types/eslint": "^9.6.1", "@types/eslint": "^9.6.0",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.20",
"eslint": "^9.29.0", "eslint": "^9.11.1",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.46.1", "eslint-plugin-svelte": "^2.36.0",
"globals": "^15.15.0", "globals": "^15.9.0",
"mdsvex": "^0.12.6", "mdsvex": "^0.12.3",
"mdsvex-relative-images": "^1.0.3", "mdsvex-relative-images": "^1.0.3",
"prettier": "^3.5.3", "prettier": "^3.1.1",
"prettier-plugin-svelte": "^3.4.0", "prettier-plugin-svelte": "^3.1.2",
"prettier-plugin-tailwindcss": "^0.6.13", "prettier-plugin-tailwindcss": "^0.6.5",
"rehype-katex-svelte": "^1.2.0", "rehype-katex-svelte": "^1.2.0",
"remark-math": "^3.0.1", "remark-math": "^3.0.0",
"svelte": "^5.34.7", "svelte": "^4.2.7",
"svelte-check": "^4.0.0", "svelte-check": "^4.0.0",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.9",
"typescript": "~5.5.4", "typescript": "5.5",
"typescript-eslint": "^8.34.1", "typescript-eslint": "^8.7.0",
"unist-util-visit": "^5.0.0", "unist-util-visit": "^5.0.0",
"vite": "^6.0.0", "vite": "^5.0.3",
"vitest": "^2.1.9" "vitest": "^2.0.0"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-free": "^6.7.2", "@fortawesome/fontawesome-free": "^6.6.0",
"bits-ui": "^2.8.0", "bits-ui": "^0.21.13",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"lucide-svelte": "^0.451.0", "lucide-svelte": "^0.451.0",
"rehype-slug": "^6.0.0", "rehype-slug": "^6.0.0",
"remark-github-blockquote-alert": "^1.3.1", "remark-github-blockquote-alert": "^1.2.1",
"remark-toc": "^9.0.0", "remark-toc": "^9.0.0",
"shiki": "^1.29.2", "shiki": "^1.22.0",
"svelte-radix": "^1.1.1", "svelte-radix": "^1.1.1",
"tailwind-merge": "^2.6.0", "tailwind-merge": "^2.5.2",
"tailwind-variants": "^0.2.1" "tailwind-variants": "^0.2.1"
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -126,7 +126,7 @@ a.disabled-link {
color: rgb(128,128,128); color: rgb(128,128,128);
pointer-events: none; pointer-events: none;
} }
main a:not(.icon) { a:not(.icon) {
position: relative; position: relative;
text-decoration: inherit; text-decoration: inherit;

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 149 KiB

View File

@ -37,7 +37,9 @@
<p class="text-gray-200 flex justify-center"> <p class="text-gray-200 flex justify-center">
{#each (post?.tags ?? []) as tag, i} {#each (post?.tags ?? []) as tag, i}
{#if i > 0} {#if i > 0}
<div class="separator text-sm">
<DividerVertical /> <DividerVertical />
</div>
{/if} {/if}
<a href="/tag/{tag}">{tag}</a> <a href="/tag/{tag}">{tag}</a>
{/each} {/each}

View File

@ -20,10 +20,7 @@
} }
</script> </script>
<button <button class="nav-link" on:click={toggleTheme} aria-label="Toggle Theme">
id="theme-switch"
class="nav-link"
on:click={toggleTheme} aria-label="Toggle Theme">
{#if $theme === "tokiwa"} {#if $theme === "tokiwa"}
<Sun /> <Sun />
{:else} {:else}
@ -33,7 +30,6 @@
<style> <style>
button { button {
flex-shrink: 0;
padding: 0; padding: 0;
font-weight: inherit; font-weight: inherit;
background: none; background: none;

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 340 KiB

View File

@ -1,39 +1,14 @@
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte';
import { derived } from 'svelte/store' import { derived } from 'svelte/store'
import { routes } from "$lib/sitemap.ts" import { routes } from "$lib/sitemap.ts"
import { Separator } from "$lib/components/ui/separator/index.js"; import { Separator } from "$lib/components/ui/separator/index.js";
import { theme, setTheme, getThemeObject } from '$lib/theme' import { theme, getThemeObject } from '$lib/theme'
import { Rss } from "lucide-svelte"; import { Rss } from "lucide-svelte";
import ThemeSwitch from "$lib/components/ThemeSwitch.svelte" import ThemeSwitch from "$lib/components/ThemeSwitch.svelte"
import Cover from "$lib/Cover.svelte"
let scrollPosition: number = .5; let scrollPosition: number = .5;
let scrollHeight: number = 1; let scrollHeight: number = 1;
let progress: number let progress: number = 0;
= 0.0;
let button_blog;
function toggleTheme() {
theme.update((currentTheme) => {
const newTheme = currentTheme === 'tokiwa' ? 'star' : 'tokiwa'
document.documentElement.setAttribute('color-scheme', newTheme)
localStorage.setItem('color-scheme', newTheme)
if (newTheme === "tokiwa") {
document.documentElement.classList.remove('dark');
} else {
document.documentElement.classList.add('dark');
}
setTheme(newTheme);
return newTheme
})
}
onMount(() => {
let entity_switch = document.getElementById("switch");
entity_switch.addEventListener("click", toggleTheme);
});
function onScroll() { function onScroll() {
if (scrollHeight > 1) { if (scrollHeight > 1) {
progress = Math.max(0, Math.min(1, scrollPosition / scrollHeight)); progress = Math.max(0, Math.min(1, scrollPosition / scrollHeight));
@ -65,6 +40,11 @@
}; };
const iconLinks: [string, string][] = Object.entries(metadata.links).map( const iconLinks: [string, string][] = Object.entries(metadata.links).map(
([key, link]) => [iconMap[key], link]); ([key, link]) => [iconMap[key], link]);
const frontCover = derived(
theme,
($theme) => getThemeObject($theme, metadata.frontCover)
)
</script> </script>
<svelte:head> <svelte:head>
@ -72,32 +52,37 @@
</svelte:head> </svelte:head>
<div <div
id="cover" id="background"
style="opacity: {1 - progress}; --grid-line: #999; --grid-line-dark: #333; --grid-cell: #707078" style="opacity: {1 - progress}; background-image: url('{$frontCover}')"
> >
<div id="cover-image">
<Cover />
</div> </div>
<div id="navbar"> <div id="front">
<div> <div id="floater">
<p class="text-2xl" style="color: #888; z-index: 2">{metadata.frontDescription}</p> <p class="text-2xl floater-elem" style="color: #888">{metadata.frontDescription}</p>
</div> <div
<div id="link-container" class="h-5 items-center"> class="floater-elem"
style="padding-bottom: {(1 - progress) * 50}vh"
>
<div id="navbar" class="h-5 items-center space-x-4 text-sm">
<a class="nav-link" href="#bio">Bio</a> <a class="nav-link" href="#bio">Bio</a>
<Separator class="bg-gray-500 dark:bg-gray-100" orientation="vertical" />
{#each routes as item} {#each routes as item}
{#if item.route !== "/"} {#if item.route !== "/"}
<a <a
href={item.route} href={item.route}
class="nav-link" class="nav-link"
class:disabled-link={item.disabled}>{item.name}</a> class:disabled-link={item.disabled}>{item.name}</a>
<Separator class="bg-gray-500 dark:bg-gray-100" orientation="vertical" />
{/if} {/if}
{/each} {/each}
<a class="icon nav-link" href="/rss.xml"><Rss /></a> <a class="icon nav-link" href="/rss.xml"><Rss /></a>
<Separator class="bg-gray-500 dark:bg-gray-100" orientation="vertical" />
<ThemeSwitch /> <ThemeSwitch />
</div> </div>
</div> </div>
</div> </div>
</div>
<main> <main>
<div id="bio"> <div id="bio">
@ -107,7 +92,7 @@
<p id="bio-name">{name}</p> <p id="bio-name">{name}</p>
<div class="w-100"> <div class="w-100">
{#each iconLinks as [cl, link]} {#each iconLinks as [cl, link]}
<a class="icon m-1" href={link} aria-label={cl}><i class="fa-xl {cl}"></i></a> <a class="icon m-1" href={link}><i class="fa-xl {cl}"></i></a>
{/each} {/each}
</div> </div>
</div> </div>
@ -129,130 +114,39 @@
/> />
<style> <style>
#cover { #background {
background: background-size: cover;
repeating-linear-gradient(128.6deg, transparent, var(--grid-line) 1px, var(--grid-line) 2px, transparent 1px, transparent 55px), background-repeat: no-repeat;
repeating-linear-gradient(11deg, transparent, var(--grid-line) 1px, var(--grid-line) 2px, transparent 1px, transparent 40px), background-position: center center;
var(--grid-cell) z-index: -10;
;
/*
position: absolute; position: absolute;
background-size: contain; min-width: 100svw;
background-position: center center; */ height: 100svh;
overscroll-behaviour: none; overscroll-behaviour: none;
}
#front {
height: 100svh;
display: flex; display: flex;
justify-content: center;
flex-direction: column; flex-direction: column;
visibility: visible; overscroll-behavior: none;
} }
:global(.dark #cover) { #floater {
background: display: flex;
repeating-linear-gradient(128.6deg, transparent, var(--grid-line-dark) 1px, var(--grid-line-dark) 2px, transparent 1px, transparent 55px), flex-direction: column;
repeating-linear-gradient(11deg, transparent, var(--grid-line-dark) 1px, var(--grid-line-dark) 2px, transparent 1px, transparent 40px), justify-content: center;
black margin-top: auto;
; margin-left: auto;
} margin-right: auto;
:global(#cover-image > svg) {
max-width: 100svw;
max-height: 100svh;
width: 100%;
height: 100%;
}
:global(svg a) {
pointer-events: all;
:global(path) {
filter: contrast(50%);
}
:global(.effect) {
visibility: hidden;
}
&:hover,
&:focus {
:global(.effect) {
visibility: visible;
}
:global(path) {
filter: contrast(100%);
}
}
}
:global(.dark .shadow) {
visibility: hidden;
}
:global(svg .shadow) {
pointer-events: none;
}
:global(#switch-right) {
visibility: hidden;
}
:global(#switch-left) {
visibility: visible;
}
:global(.dark #switch-right) {
visibility: visible;
}
:global(.dark #switch-left) {
visibility: hidden;
}
:global(.effect > .glow) {
filter: blur(.3rem);
}
.nav-link {
display: inline-block;
} }
#navbar { #navbar {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
padding-top: 30%; justify-content: center;
flex-direction: column;
div {
padding-left: 5%;
} }
.floater-elem {
margin-left: auto;
margin-right: auto;
} }
#link-container {
display: flex;
flex-direction: column;
align-items: flex-start;
}
/* mobile */
@media (max-width: 700px) {
#navbar {
visibility: visible;
height: 100svh;
}
#cover {
display: flex;
flex-direction: column;
justify-content: flex-end;
}
#cover-image {
position: absolute;
max-height: 100svh;
max-width: 100svw;
pointer-events: none;
:global(.effect) {
visibility: visible;
}
}
:global(#cover-image a) {
pointer-events: none;
}
:global(svg .entity-label) {
visibility: hidden;
}
}
/* desktop */
@media (min-width: 701px) {
#navbar {
padding-top: 0;
visibility: hidden;
height: 0;
width: 0;
}
}
/* main */
main { main {
width: 100%; width: 100%;
@apply bg-background; @apply bg-background;

View File

@ -1,34 +0,0 @@
User-agent: GPTBot
Disallow: /
User-agent: ChatGPT-User
Disallow: /
User-agent: Google-Extended
Disallow: /
User-agent: PerplexityBot
Disallow: /
User-agent: Amazonbot
Disallow: /
User-agent: ClaudeBot
Disallow: /
User-agent: Omgilibot
Disallow: /
User-Agent: FacebookBot
Disallow: /
User-Agent: Applebot
Disallow: /
User-agent: anthropic-ai
Disallow: /
User-agent: Bytespider
Disallow: /
User-agent: Claude-Web
Disallow: /
User-agent: Diffbot
Disallow: /
User-agent: ImagesiftBot
Disallow: /
User-agent: Omgilibot
Disallow: /
User-agent: Omgili
Disallow: /
User-agent: YouBot
Disallow: /