Compare commits

..

No commits in common. "main" and "post/rss" have entirely different histories.

47 changed files with 5777 additions and 10478 deletions

View File

@ -11,7 +11,7 @@ repos:
hooks:
- id: conventional-pre-commit
stages: [commit-msg]
args: [feat, fix, chore, test, build, merge, refactor, doc]
args: [feat, fix, chore, test, build, merge]
- repo: local
hooks:
- id: eslint

View File

@ -7,10 +7,10 @@ A blog framework
To create the production version,
```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
@ -19,39 +19,28 @@ To insert custom content, create a directory with a similar structure to
directory. The website will be built statically in `build/`. Note that changing
`favicon.svg` will require modifying the `static/` folder.
Set `metadata.json` based on the example to have a custom avatar and background
image.
- `name`: Displayed name on the home page
- `blogName`: Displayed name of the blog
- `url`: The site url
- `frontDescription`: The text above the main navbar
- `frontCover`: Path to the cover image
- `picture`: Avatar
- `links`: A map of brands to links
- `extra`: Extra pages showing on the navbar
- `markdown`: `{ "highlighterLangs": [ ... ], "katex": [ ... ] }`
## Developing
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
pnpm run dev [-- --open]
npm run dev [-- --open]
```
### Formatting
``` bash
pnpx eslint
npx eslint
```
### Testing
To run tests
``` bash
pnpx playwright test
npx playwright test
```
All tests must have the suffix `.spec.[jt]s`. Be cautious while updating

5239
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "chrysoblog",
"version": "0.0.4",
"version": "0.0.2",
"private": true,
"scripts": {
"dev": "vite dev",
@ -16,47 +16,44 @@
"prepare": "svelte-kit sync"
},
"devDependencies": {
"@eslint/js": "^9.29.0",
"@playwright/test": "^1.53.1",
"@sveltejs/adapter-auto": "^3.3.1",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.22.0",
"@sveltejs/vite-plugin-svelte": "^5.1.0",
"@tailwindcss/typography": "^0.5.16",
"@types/eslint": "^9.6.1",
"autoprefixer": "^10.4.21",
"eslint": "^9.29.0",
"@eslint/js": "^9.11.1",
"@playwright/test": "^1.40.0",
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-static": "^3.0.5",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@tailwindcss/typography": "^0.5.15",
"@types/eslint": "^9.6.0",
"autoprefixer": "^10.4.20",
"eslint": "^9.11.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.46.1",
"globals": "^15.15.0",
"mdsvex": "^0.12.6",
"eslint-plugin-svelte": "^2.36.0",
"globals": "^15.9.0",
"mdsvex": "^0.12.3",
"mdsvex-relative-images": "^1.0.3",
"prettier": "^3.5.3",
"prettier-plugin-svelte": "^3.4.0",
"prettier-plugin-tailwindcss": "^0.6.13",
"rehype-katex-svelte": "^1.2.0",
"remark-math": "^3.0.1",
"svelte": "^5.34.7",
"prettier": "^3.1.1",
"prettier-plugin-svelte": "^3.1.2",
"prettier-plugin-tailwindcss": "^0.6.5",
"svelte": "^4.2.7",
"svelte-check": "^4.0.0",
"tailwindcss": "^3.4.17",
"typescript": "~5.5.4",
"typescript-eslint": "^8.34.1",
"tailwindcss": "^3.4.9",
"typescript": "5.5",
"typescript-eslint": "^8.7.0",
"unist-util-visit": "^5.0.0",
"vite": "^6.0.0",
"vitest": "^2.1.9"
"vite": "^5.0.3",
"vitest": "^2.0.0"
},
"type": "module",
"dependencies": {
"@fortawesome/fontawesome-free": "^6.7.2",
"bits-ui": "^2.8.0",
"@fortawesome/fontawesome-free": "^6.6.0",
"bits-ui": "^0.21.13",
"clsx": "^2.1.1",
"lucide-svelte": "^0.451.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",
"shiki": "^1.29.2",
"svelte-radix": "^1.1.1",
"tailwind-merge": "^2.6.0",
"tailwind-merge": "^2.5.2",
"tailwind-variants": "^0.2.1"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,5 @@
@import url(./style/content.css);
@import url(./style/code.css);
@import '@fortawesome/fontawesome-free/css/all.min.css';
@tailwind base;
@tailwind components;
@ -36,16 +37,11 @@
--ring: 20 14.3% 4.1%;
--radius: 0.5rem;
--shadow: theme('colors.java.800');
--name: theme('colors.java.800');
--navlink: theme('colors.eucalyptus.400');
--series: theme('colors.sunglow.700');
}
.dark {
--background: 20 14.3% 4.1%;
--foreground: 150 9.1% 70%;
--foreground: 60 9.1% 97.8%;
--muted: 12 6.5% 15.1%;
--muted-foreground: 24 5.4% 63.9%;
@ -72,17 +68,9 @@
--destructive-foreground: 60 9.1% 97.8%;
--ring: 24 5.7% 82.9%;
--shadow: theme('colors.java.100');
--name: theme('colors.java.300');
--navlink: theme('colors.eucalyptus.200');
--series: theme('colors.sunglow.600');
}
}
body {
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
}
@layer base {
* {
@apply border-border;
@ -103,73 +91,3 @@ html {
hr {
@apply separator;
}
a, button {
color: theme('colors.eucalyptus.400');
&:hover,
&:focus {
color: theme('colors.java.500');
}
}
.dark a:not(.disabled-link),button {
text-shadow: 0 0 5px;
}
.nav-link, .nav-link * {
@apply text-lg;
font-weight: 500;
font-family: serif;
}
.nav-link:not(.disabled-link):not(.current-link) {
color: var(--navlink);
}
a.disabled-link {
color: rgb(128,128,128);
pointer-events: none;
}
main a:not(.icon) {
position: relative;
text-decoration: inherit;
&::after {
content: "";
display: block;
width: 100%;
position: absolute;
height: 2px;
bottom: 0;
left: 0;
background-color: theme('colors.java.500');
transform: scaleX(0);
transition: transform 0.3s ease;
}
&:hover,
&:focus {
&::after {
content: "";
position: absolute;
transform: scaleX(1);
}
}
}
a.current-link {
color: theme('colors.magenta.500');
&::after {
background-color: theme('colors.magenta.500');
}
&:hover,
&:focus {
color: theme('colors.magenta.500');
}
}
html.dark .shiki,
html.dark .shiki span {
color: var(--shiki-dark) !important;
background-color: var(--shiki-dark-bg) !important;
/* Optional, if you also want font styles */
font-style: var(--shiki-dark-font-style) !important;
font-weight: var(--shiki-dark-font-weight) !important;
text-decoration: var(--shiki-dark-text-decoration) !important;
}

View File

@ -5,8 +5,6 @@
<link rel="icon" href="%sveltekit.assets%/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css" integrity="sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X" crossorigin="anonymous">
<meta name="theme-color" content="#028760" />
</head>
<body data-sveltekit-preload-data="hover" class="font-serif">
<div style="display: contents">%sveltekit.body%</div>

View File

@ -1,6 +0,0 @@
---
title: Research
---
# Research
I teach Lean!

View File

@ -17,7 +17,7 @@ Strongest of Gensokyo
# Projects
> People say nothing is impossible but I do nothing everyday. -- Winnie The Pooh
> This is a quote
```python
for i in range(9):

View File

@ -12,12 +12,5 @@
},
"extra": [
{ "route": "/archives", "name": "Archives" }
],
"markdown": {
"highlighterLangs": [
"python",
"lean",
"tex"
]
}
]
}

View File

@ -7,7 +7,7 @@ series: ["placeholder"]
---
# 1st Level Heading
Here is some placeholder text. Here is some **bold text**.
Here is some placeholder text
## 2nd Level Heading

View File

@ -3,6 +3,6 @@ title: Placeholder 2
date: '2024-09-20'
description: "This is a placeholder description"
tags: ["a123"]
series: ["placeholder", "another-series"]
series: ["placeholder"]
---
## Content

View File

@ -1,6 +1,7 @@
---
title: Placeholder 3
date: '2024-09-24'
description: "This is a placeholder description"
tags: []
series: ["placeholder"]
---

View File

@ -4,29 +4,3 @@ date: '2024-09-15'
tags: ["Cirno"]
---
# Content
Here are some contents for the perfect math class. [A link](https://example.com)
```lean
theorem perfect_number_theorem (n: Nat),
Nat.is_odd → ¬perfect n := by
intro h
apply cirno_theorem
```
> :>WARNING
> The proof is elusive!
Some KaTeX: $a^n + b^n = c^n$.
$$
\Gamma(z) = \int_0^\infty e^{-t} t^{z-1}\,\mathrm dz
$$
$$
\vec z^{(l+1)} = \mat M\vec z^{(l)} + \vec b
$$
```tex
\\newcommand{\\cirno}{9}
```

View File

@ -1,6 +1,7 @@
import type { Reroute } from '@sveltejs/kit';
const translated: Record<string, string> = {
'/post': '/page/1',
};
export const reroute: Reroute = ({ url }) => {

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 149 KiB

View File

@ -1,7 +1,6 @@
<script lang="ts">
import type { Post } from '$lib/types'
import Dot from "svelte-radix/Dot.svelte"
import DividerVertical from "svelte-radix/DividerVertical.svelte"
import DividerVertical from "svelte-radix/DividerVertical.svelte";
export let post: Post;
export let inactive: bool = false;
@ -10,34 +9,26 @@
</script>
<div id="post-heading">
{#if series}
<div class="flex flex-row justify-left items-center">
{#each series as seriesTag, i}
{#if i > 0}
<div class="separator text-sm">
<Dot />
</div>
{/if}
<p class="series-tag">{seriesTag}</p>
{/each}
</div>
{/if}
<h2>
{#each series as seriesTag}
<p class="series-tag">{seriesTag}</p>
{/each}
{#if inactive}
<p id="post-title">{post.title}</p>
{:else}
<a id="post-title" href="/post/{post.slug}">{post.title}</a>
{/if}
</h2>
<!-- Visually this component should exist to ensure the even spacing between individual posts. -->
<div>
<p class="text-gray-500 h-8">{post?.description || ""}</p>
<p class="text-gray-500 h-8">{post.description}</p>
</div>
<div class="flex flex-row justify-between" style="width: 100%">
<p class="text-gray-200 flex justify-center">
{#each (post?.tags ?? []) as tag, i}
{#if i > 0}
<div class="separator text-sm">
<DividerVertical />
</div>
{/if}
<a href="/tag/{tag}">{tag}</a>
{/each}
@ -55,6 +46,6 @@
@apply text-3xl;
}
.series-tag {
color: var(--series);
color: theme('colors.sunglow.600');
}
</style>

View File

@ -1,69 +0,0 @@
<script lang="ts">
import { DoubleArrowLeft, DoubleArrowRight, ChevronLeft, ChevronRight } from 'svelte-radix';
import type { Post } from '$lib/types'
import PostHeader from './PostHeader.svelte';
export let posts: Post[];
// The page navigation bar will not show if these numbers are not provided.
export let maxPageN: number = 0;
export let pageN: number = 0;
const disableLinkPrev = pageN == 1;
const disableLinkNext = pageN == maxPageN;
const firstPageLink = "/post";
const prevLink = pageN <= 2 ? firstPageLink : `/page/${pageN - 1}`;
</script>
<ul id="catalog" class="content">
{#each posts as post}
<li>
<PostHeader {post}/>
</li>
{/each}
</ul>
<!-- Navigation bar -->
{#if maxPageN}
<hr class="separator" />
<div id="page-navigator">
<a
aria-label="First Page"
class="nav-link icon"
class:disabled-link={disableLinkPrev}
href={firstPageLink}><DoubleArrowLeft /></a>
<a
aria-label="Prev Page"
class="nav-link icon"
class:disabled-link={disableLinkPrev}
href={prevLink}><ChevronLeft /></a>
<div id="page-map">
<p id="page-num">{pageN}/{maxPageN}</p>
</div>
<a
aria-label="Next Page"
class="nav-link icon"
class:disabled-link={disableLinkNext}
href="/page/{Math.min(maxPageN, pageN+1)}"><ChevronRight /></a>
<a
aria-label="Last Page"
class="nav-link icon"
class:disabled-link={disableLinkNext}
href="/page/{maxPageN}"><DoubleArrowRight /></a>
</div>
{/if}
<style>
#page-navigator {
display: flex;
flex-direction: horizontal;
justify-content: space-between;
margin: 1em auto 1em auto;
width: 80%;
align-items: center;
}
#page-map {
display: inline-block;
}
#page-num {
color: rgb(128,128,128);
}
</style>

View File

@ -1,44 +0,0 @@
<script lang="ts">
import { Moon, Sun } from 'lucide-svelte';
import { theme, setTheme } from '$lib/theme'
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
})
}
</script>
<button
id="theme-switch"
class="nav-link"
on:click={toggleTheme} aria-label="Toggle Theme">
{#if $theme === "tokiwa"}
<Sun />
{:else}
<Moon />
{/if}
</button>
<style>
button {
flex-shrink: 0;
padding: 0;
font-weight: inherit;
background: none;
border: none;
box-shadow: none;
overflow: hidden;
}
</style>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 340 KiB

View File

@ -15,8 +15,7 @@ export function transformPostMeta(metadata: PostMetadata, slug: string) : Post {
};
}
// Generates a list of all posts filtering by tags and series, reverse chronologically ordered.
export async function getPosts(filter: { tag?: string, series?: string }) : Promise<Post[]> {
export async function getPosts(tag: string | null = null) : Promise<Post[]> {
const allPostFiles = import.meta.glob('$content/post/*.md');
const iterablePostFiles = Object.entries(allPostFiles);
@ -28,10 +27,8 @@ export async function getPosts(filter: { tag?: string, series?: string }) : Prom
return transformPostMeta(metadata, slug);
})
);
if (filter?.tag)
posts = posts.filter(post => post.tags.includes(filter.tag!))
if (filter?.series)
posts = posts.filter(post => post.series?.includes(filter.series!) ?? false)
if (tag)
posts = posts.filter(post => post.tags.includes(tag))
posts.sort((post1, post2) => {
const date1: Date = post1.date;

View File

@ -1,8 +1,8 @@
import siteMetadata from '$content/metadata.json';
export const routes: { route: string, name: string, disabled?: boolean }[] = [
{ route: "/", name: "Home" },
{ route: "/post", name: "Blog" },
{ route: "/tag", name: "Tags" },
{ route: "/gallery", name: "Gallery", disabled: true },
{ route: "/research", name: "Research" },
{ route: "/archives", name: "Archives" },
]
].concat(siteMetadata?.extra || []);

View File

@ -1,21 +0,0 @@
import { writable, get } from 'svelte/store'
import { browser } from "$app/environment"
type Theme = 'tokiwa' | 'star'
const userTheme = browser && localStorage.getItem('color-scheme')
export const theme = writable(userTheme ?? 'tokiwa')
// set the theme
export function setTheme(newTheme: Theme) {
theme.set(newTheme)
}
export function getThemeObject(key: string, obj: { [key in Theme]: string } | string) {
if (typeof obj === 'object') {
return obj[key];
} else {
return obj;
}
}

View File

@ -1,7 +1,6 @@
<script lang="ts">
import '../app.css';
import { page } from '$app/stores';
import { name } from '$content/metadata.json';
import Footer from "./Footer.svelte";
import Navbar from "./Navbar.svelte";
import PageTransition from "./PageTransition.svelte";
@ -12,8 +11,7 @@
<!-- Homepage has its own navbar with animations -->
{#if $page.url.pathname != "/"}
<div id="layout">
<div id="nav">
<p id="bio-name">{name}</p>
<div id="navbar">
<Navbar />
<slot name="header" />
</div>
@ -33,11 +31,58 @@
{/if}
<style>
#bio-name {
:global(.nav-link) {
@apply text-lg;
font-weight: 500;
font-family: serif;
font-size: 3rem;
font-weight: normal;
color: var(--name);
}
:global(a.disabled-link) {
color: rgb(128,128,128);
pointer-events: none;
}
:global(a) {
color: theme('colors.eucalyptus.400');
&:hover,
&:focus {
color: theme('colors.java.500');
}
}
:global(a):not(.icon) {
position: relative;
text-decoration: inherit;
&::after {
content: "";
display: block;
width: 100%;
position: absolute;
height: 2px;
bottom: 0;
left: 0;
background-color: theme('colors.java.500');
transform: scaleX(0);
transition: transform 0.3s ease;
}
&:hover,
&:focus {
&::after {
content: "";
position: absolute;
transform: scaleX(1);
}
}
}
:global(a.current-link) {
color: theme('colors.magenta.500');
&::after {
background-color: theme('colors.magenta.500');
}
&:hover,
&:focus {
color: theme('colors.magenta.500');
}
}
#layout {
max-width: max(80vw, 100vh);
@ -66,7 +111,7 @@
width: min(100vw, max(50vw, 100vh));
grid-area: content;
}
#nav {
#navbar {
align-self: right;
padding-left: 10px;
grid-area: nav;

View File

@ -1,39 +1,11 @@
<script lang="ts">
import { onMount } from 'svelte';
import { derived } from 'svelte/store'
import { routes } from "$lib/sitemap.ts"
import { Separator } from "$lib/components/ui/separator/index.js";
import { theme, setTheme, getThemeObject } from '$lib/theme'
import { Rss } from "lucide-svelte";
import ThemeSwitch from "$lib/components/ThemeSwitch.svelte"
import Cover from "$lib/Cover.svelte"
let scrollPosition: number = .5;
let scrollHeight: number = 1;
let progress: number
= 0.0;
let progress: number = 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() {
if (scrollHeight > 1) {
progress = Math.max(0, Math.min(1, scrollPosition / scrollHeight));
@ -46,22 +18,15 @@
import metadata from '$content/metadata.json';
const iconMap: Map<string, string> = {
"orcid": "fa-brands fa-orcid",
"google-scholar": "fa-brands fa-google-scholar",
"bitbucket": "fa-brands fa-bitbucket",
"discord": "fa-brands fa-discord",
"email": "fa-solid fa-envelope",
"bitbucket": "fa-brands fa-bitbucket",
"github": "fa-brands fa-github",
"gitlab": "fa-brands fa-gitlab",
"open-source": "fa-brands fa-osi",
"bluesky": "fa-brands fa-bluesky",
"youtube": "fa-brands fa-youtube",
"google-scholar": "fa-brands fa-google-scholar",
"instagram": "fa-brands fa-instagram",
"bandcamp": "fa-brands fa-bandcamp",
"pixiv": "fa-brands fa-pixiv",
"open-source": "fa-brands fa-osi",
"orcid": "fa-brands fa-orcid",
};
const iconLinks: [string, string][] = Object.entries(metadata.links).map(
([key, link]) => [iconMap[key], link]);
@ -72,42 +37,42 @@
</svelte:head>
<div
id="cover"
style="opacity: {1 - progress}; --grid-line: #999; --grid-line-dark: #333; --grid-cell: #707078"
id="background"
style="opacity: {1 - progress}; background-image: url('{metadata.frontCover}')"
>
<div id="cover-image">
<Cover />
</div>
</div>
<div id="navbar">
<div>
<p class="text-2xl" style="color: #888; z-index: 2">{metadata.frontDescription}</p>
</div>
<div id="link-container" class="h-5 items-center">
<a class="nav-link" href="#bio">Bio</a>
{#each routes as item}
{#if item.route !== "/"}
<a
href={item.route}
class="nav-link"
class:disabled-link={item.disabled}>{item.name}</a>
{/if}
{/each}
<a class="icon nav-link" href="/rss.xml"><Rss /></a>
<ThemeSwitch />
<div id="front">
<div id="floater">
<p class="text-2xl floater-elem" style="color: #888">{metadata.frontDescription}</p>
<div
class="floater-elem"
style="padding-bottom: {(1 - progress) * 50}vh"
>
<div id="navbar" class="h-5 items-center space-x-4 text-sm">
<a href="#bio">Bio</a>
<Separator class="bg-gray-500 dark:bg-gray-100" orientation="vertical" />
{#each routes as item}
{#if item.route !== "/"}
<a href={item.route} class:disabled-link={item.disabled}>{item.name}</a>
<Separator class="bg-gray-500 dark:bg-gray-100" orientation="vertical" />
{/if}
{/each}
<a class="icon" href="/rss.xml"><Rss /></a>
</div>
</div>
</div>
</div>
<main>
<div id="bio">
<div id="bio" class="bg-background">
<div class="p-1">
<div class="flex">
<div id="info flex-col">
<p id="bio-name">{name}</p>
<div class="w-100">
{#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}
</div>
</div>
@ -116,7 +81,7 @@
</div>
</div>
</div>
<article id="bio-core" class="prose lg:prose-xl dark:prose-dark">
<article id="bio-core" class="prose lg:prose-xl dark:prose-invert">
<Content />
</article>
</div>
@ -129,136 +94,45 @@
/>
<style>
#cover {
background:
repeating-linear-gradient(128.6deg, transparent, var(--grid-line) 1px, var(--grid-line) 2px, transparent 1px, transparent 55px),
repeating-linear-gradient(11deg, transparent, var(--grid-line) 1px, var(--grid-line) 2px, transparent 1px, transparent 40px),
var(--grid-cell)
;
/*
position: absolute;
background-size: contain;
background-position: center center; */
#background {
background-attachment: fixed;
background-size: cover;
background-repeat: no-repeat;
background-position: center center;
z-index: -10;
position: fixed;
min-width: 100%;
min-height: 100%;
overscroll-behaviour: none;
}
#front {
height: 100vh;
display: flex;
justify-content: center;
flex-direction: column;
visibility: visible;
overscroll-behavior: none;
}
:global(.dark #cover) {
background:
repeating-linear-gradient(128.6deg, transparent, var(--grid-line-dark) 1px, var(--grid-line-dark) 2px, transparent 1px, transparent 55px),
repeating-linear-gradient(11deg, transparent, var(--grid-line-dark) 1px, var(--grid-line-dark) 2px, transparent 1px, transparent 40px),
black
;
}
: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;
#floater {
display: flex;
flex-direction: column;
justify-content: center;
margin-top: auto;
margin-left: auto;
margin-right: auto;
}
#navbar {
display: flex;
flex-wrap: wrap;
padding-top: 30%;
flex-direction: column;
div {
padding-left: 5%;
}
justify-content: center;
}
#link-container {
display: flex;
flex-direction: column;
align-items: flex-start;
.floater-elem {
margin-left: auto;
margin-right: auto;
}
/* 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 {
width: 100%;
@apply bg-background;
@apply dark:bg-gray-800;
/*box-shadow:
0px -5px 5px var(--shadow);*/
box-shadow:
0px -5px 10px 10px rgba(200,200,200,0.4);
}
#bio {
max-width: max(50vw, 100vh);
@ -271,14 +145,14 @@
font-family: serif;
font-size: min(10vw, 60px);
display: inline-block;
color: var(--name);
color: theme('colors.java.800');
}
#avatar {
line-height: 0;
display: inline-block;
margin: 5px;
margin-right: auto;
border: 1px solid var(--name);
border: 4px solid theme('colors.eucalyptus.500');
border-radius: 50%;
aspect-ratio: 1;
max-width: 20%;
@ -297,6 +171,7 @@
margin-left: 50px;
}
#bio-core > :global(h1) {
color: theme('colors.eucalyptus.700');
font-weight: normal;
border-bottom: 1px solid;
}

View File

@ -7,7 +7,6 @@
text-align: center;
bottom: 10px;
font-size: 12px;
background-color: var(--background);
color: var(--foreground);
color: #888;
}
</style>

View File

@ -1,95 +1,41 @@
<script lang="ts">
import { page } from '$app/stores';
import { routes } from "$lib/sitemap.ts"
import ThemeSwitch from "$lib/components/ThemeSwitch.svelte"
import { Rss, Menu } from "lucide-svelte";
import { name } from '$content/metadata.json';
import { Rss } from "lucide-svelte";
function isCurrentLink(pathname, route) {
return route != "/" && pathname.startsWith(route)
|| route == "/post" && pathname.startsWith("/page");
}
import { onMount } from 'svelte';
let showDropdown = false;
function onDropdownToggle(event) {
showDropdown = !showDropdown;
}
function onKeyDown(event) {
if (event.key == "Escape") {
showDropdown = !showDropdown;
}
}
</script>
<nav
id="navbar"
class="h-5 space-x-4"
class:dropdown-opened={showDropdown}>
<div
class="link-container"
role="button"
tabindex={0}
on:keydown={onKeyDown}
on:click={() => { showDropdown = false; }}
>
{#each routes as item}
<a
class="nav-link"
class:disabled-link={item.disabled}
class:current-link={isCurrentLink($page.url.pathname, item.route)}
on:click={() => { showDropdown = false; }}
href={item.route}>
{item.name}
</a>
{/each}
<a class="nav-link icon" href="/rss.xml"><Rss /></a>
<div><ThemeSwitch /></div>
</div>
<button
id="dropdown-toggle"
aria-hidden="true"
on:click={onDropdownToggle}>
<Menu />
</button>
</nav>
<svelte:window on:keydown={onKeyDown} />
<div id="navbar" class="h-5 space-x-4">
<p id="bio-name">{name}</p>
{#each routes as item}
<a
class="nav-link"
class:current-link={isCurrentLink($page.url.pathname, item.route)}
class:disabled-link={item.disabled}
href={item.route}>{item.name}</a>
{/each}
<a class="nav-link icon" href="/rss.xml"><Rss /></a>
</div>
<style>
.link-container {
#bio-name {
font-family: serif;
font-size: 3rem;
font-weight: normal;
color: theme('colors.java.800');
}
#navbar {
display: flex;
flex-direction: column;
align-items: flex-end;
position: sticky;
top: 0;
justify-content: between;
}
#dropdown-toggle {
display: none;
}
/* mobile mode */
@media (max-width: 768px) {
.link-container {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 100vh;
justify-content: center;
margin: 0;
padding-left: 5vw;
padding-right: 5vw;
background: hsl(var(--background) / .8);
/* Initial hidden state */
opacity: 0;
transform: translateY(-100%);
transition: transform 0.2s, opacity 0.2s;
}
@media (max-width: 700px) {
#navbar {
display: inline-block;
white-space: pre-wrap;
@ -97,14 +43,5 @@
#navbar .nav-link {
display: inline-block;
}
#dropdown-toggle {
display: initial;
z-index: 5;
}
.dropdown-opened > .link-container {
opacity: 1;
z-index: 1;
transform: translateY(0);
}
}
</style>

View File

@ -1,10 +1,10 @@
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
const { metadata, Content } = data;
const { slug, metadata, Content } = data;
import siteMetadata from '$content/metadata.json';
const title = metadata?.title
const title = metadata?.title || (slug[0].toUpperCase() + slug.slice(1));
</script>
<svelte:head>
@ -12,7 +12,7 @@
</svelte:head>
<hr />
<article class="prose lg:prose-xl dark:prose-dark p-4">
<article class="prose lg:prose-xl dark:prose-invert p-4">
<Content />
</article>
<hr />

View File

@ -0,0 +1,12 @@
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ params }) => {
const post = await import(`$content/extra/${params.slug}.md`);
const Content = post.default;
return {
slug: params.slug,
metadata: post.metadata,
Content,
};
}

View File

@ -1,14 +0,0 @@
import { error } from '@sveltejs/kit';
import type { PageLoad } from './$types';
export const load: PageLoad = async (_) => {
const post = await import(`$content/extra/archives.md`);
if (!post) throw error(404);
const { name } = post.metadata;
const Content = post.default;
return {
name,
Content,
};
}

View File

@ -1,10 +1,14 @@
<script lang="ts">
import siteMetadata from '$content/metadata.json'
import PostList from '$lib/components/PostList.svelte'
import type { PageData } from './$types';
export let data: PageData;
const { posts, pageN, maxPageN } = data;
import siteMetadata from '$content/metadata.json';
import type { PageData } from './$types'
export let data: PageData
const { posts, pageN, maxPageN } = data
import { DoubleArrowLeft, DoubleArrowRight, ChevronLeft, ChevronRight } from 'svelte-radix';
import PostHeader from '$lib/components/PostHeader.svelte';
const disableLinkPrev = pageN == 1;
const disableLinkNext = pageN == maxPageN;
</script>
<svelte:head>
@ -12,7 +16,49 @@
</svelte:head>
<hr class="separator" />
<PostList {posts} {pageN} {maxPageN}/>
<ul id="catalog" class="content">
{#each posts as post}
<li>
<PostHeader post={post}/>
</li>
{/each}
</ul>
<hr class="separator" />
<div id="page-navigator">
<a
class="nav-link icon"
class:disabled-link={disableLinkPrev}
href="/page/1"><DoubleArrowLeft /></a>
<a
class="nav-link icon"
class:disabled-link={disableLinkPrev}
href="/page/{Math.max(1, pageN-1)}"><ChevronLeft /></a>
<div id="page-map">
<p id="page-num">{pageN}/{maxPageN}</p>
</div>
<a
class="nav-link icon"
class:disabled-link={disableLinkNext}
href="/page/{Math.min(maxPageN, pageN+1)}"><ChevronRight /></a>
<a
class="nav-link icon"
class:disabled-link={disableLinkNext}
href="/page/{maxPageN}"><DoubleArrowRight /></a>
</div>
<style>
#page-navigator {
display: flex;
flex-direction: horizontal;
justify-content: space-between;
margin: 1em auto 1em auto;
width: 80%;
align-items: center;
}
#page-map {
display: inline-block;
}
#page-num {
color: rgb(128,128,128);
}
</style>

View File

@ -1,7 +1,7 @@
import { error } from '@sveltejs/kit'
import type { PageLoad } from './$types'
import { getPosts } from '$lib/posts'
import siteMetadata from '$content/metadata.json'
import { error } from '@sveltejs/kit';
import type { PageLoad } from './$types';
import { getPosts } from '$lib/posts';
import siteMetadata from '$content/metadata.json';
export const load: PageLoad = async ({ params }) => {
const pageN: number = +params.slug;

View File

@ -1,14 +1,7 @@
import type { PageLoad } from './$types'
import { getPosts } from '$lib/posts'
import siteMetadata from '$content/metadata.json'
import type { PageLoad } from './$types';
import { getPosts } from '$lib/posts';
export const load: PageLoad = async () => {
const posts = await getPosts();
const pageSize = siteMetadata?.pageSize || 3;
const maxPageN = Math.ceil(posts.length / pageSize);
return {
maxPageN,
posts: posts.slice(0, pageSize),
};
}
export const load: PageLoad = async (_) => {
const allPosts = await getPosts();
return { allPosts };
};

View File

@ -1,10 +1,9 @@
<script lang="ts">
import siteMetadata from '$content/metadata.json';
import PostList from '$lib/components/PostList.svelte'
import type { PageData } from './$types';
export let data: PageData;
const { posts, maxPageN } = data;
const { allPosts } = data;
import siteMetadata from '$content/metadata.json';
import PostHeader from '$lib/components/PostHeader.svelte';
</script>
<svelte:head>
@ -12,4 +11,17 @@
</svelte:head>
<hr class="separator" />
<PostList {posts} pageN={1} {maxPageN}/>
<ul id="catalog" class="content">
{#each allPosts as post}
<li>
<PostHeader post={post}/>
</li>
{/each}
</ul>
<hr class="separator" />
<style>
#catalog li {
margin-bottom: 10px;
}
</style>

View File

@ -1,8 +1,7 @@
<script lang="ts">
import { TrackPrevious, TrackNext } from 'svelte-radix';
import type { PageData } from './$types';
export let data: PageData;
const { post, Content, seriesNeighbours } = data;
const { post, Content } = data;
import siteMetadata from '$content/metadata.json';
import PostHeader from '$lib/components/PostHeader.svelte';
</script>
@ -14,43 +13,7 @@
<PostHeader post={post} inactive={true}/>
<hr />
<article class="prose lg:prose-xl dark:prose-dark p-4">
<article class="prose lg:prose-xl dark:prose-invert p-4">
<Content />
</article>
<hr />
<ul id="series">
{#each seriesNeighbours as { name, prevSlug, prevTitle, nextSlug, nextTitle }}
<li class="series-neighbour">
{#if prevTitle}
<a href="/post/{prevSlug}">{prevTitle}</a>
<p class="dummy"><TrackPrevious /></p>
{/if}
<p class="series-tag">{name}</p>
{#if nextTitle}
<p class="dummy"><TrackNext /></p>
<a href="/post/{nextSlug}">{nextTitle}</a>
{/if}
</li>
{/each}
</ul>
<style>
#series {
text-align: center;
}
.series-tag {
color: var(--series);
display: inline-block;
}
.dummy {
display: inline-block;
color: rgba(128,128,128, .6);
vertical-align: middle;
padding-left: 1em;
padding-right: 1em;
}
.series-neighbour {
padding-left: 2em;
padding-right: 2em;
}
</style>

View File

@ -1,54 +1,16 @@
import { error } from '@sveltejs/kit';
import type { PageLoad } from './$types';
import type { Post } from '$lib/types'
import { getPosts, transformPostMeta } from '$lib/posts';
import { transformPostMeta } from '$lib/posts';
export const load: PageLoad = async ({ params }) => {
try {
const post = await import(`$content/post/${params.slug}.md`);
if (!post) throw error(404);
const metadata: Post = transformPostMeta(post.metadata, params.slug);
const Content = post.default;
const seriesNeighbours : {
name: string,
prevSlug?: string,
prevTitle?: string,
nextSlug?: string,
nextTitle?: string,
}[] = await Promise.all((metadata.series || []).map(async (series: string) => {
const postInSeries: Post[] = await getPosts({ series });
let prevSlug = undefined;
let prevTitle = undefined;
let nextSlug = undefined;
let nextTitle = undefined;
for (const p of postInSeries) {
if (p.date > metadata.date) {
nextSlug = p.slug;
nextTitle = p.title;
}
if (p.date < metadata.date) {
prevSlug = p.slug;
prevTitle = p.title;
break;
}
}
return {
name: series,
prevSlug,
prevTitle,
nextSlug,
nextTitle,
};
}));
return {
post: metadata,
post: transformPostMeta(post.metadata, params.slug),
Content,
seriesNeighbours,
};
}
catch {

View File

@ -1,18 +0,0 @@
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
const { metadata, Content } = data;
import siteMetadata from '$content/metadata.json';
const title = metadata?.title
</script>
<svelte:head>
<title>{title} | {siteMetadata.blogName}</title>
</svelte:head>
<hr />
<article class="prose lg:prose-xl dark:prose-dark p-4">
<Content />
</article>
<hr />

View File

@ -1,14 +0,0 @@
import { error } from '@sveltejs/kit';
import type { PageLoad } from './$types';
export const load: PageLoad = async (_) => {
const post = await import(`$content/extra/research.md`);
if (!post) throw error(404);
const { name } = post.metadata;
const Content = post.default;
return {
name,
Content,
};
}

View File

@ -1,9 +1,9 @@
<script lang="ts">
import type { PageData } from './$types';
import PostList from '$lib/components/PostList.svelte'
import siteMetadata from '$content/metadata.json';
export let data: PageData;
const { name, posts } = data;
import siteMetadata from '$content/metadata.json';
import PostHeader from '$lib/components/PostHeader.svelte';
</script>
<svelte:head>
@ -11,9 +11,19 @@
</svelte:head>
<h1>{name} ({posts.length})</h1>
<hr class="separator" />
<PostList {posts}/>
<ul id="catalog" class="content">
{#each posts as post}
<li>
<PostHeader post={post}/>
</li>
{/each}
</ul>
<hr class="separator" />
<style>
#catalog li {
margin-bottom: 10px;
}
</style>

View File

@ -2,7 +2,7 @@ import type { PageLoad } from './$types';
import { getPosts } from '$lib/posts';
export const load: PageLoad = async ({ params }) => {
const posts = await getPosts({ tag: params.slug });
const posts = await getPosts(params.slug);
return {
name: params.slug,
posts,

209
src/style/code.css Normal file
View File

@ -0,0 +1,209 @@
/* Adapted from https://github.com/PrismJS/prism-themes/blob/master/themes/prism-material-light.css */
code[class*="language-"],
pre[class*="language-"] {
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
color: #90a4ae;
background: #fafafa;
font-family: Roboto Mono, monospace;
font-size: 1em;
line-height: 1.5em;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
code[class*="language-"]::-moz-selection,
pre[class*="language-"]::-moz-selection,
code[class*="language-"] ::-moz-selection,
pre[class*="language-"] ::-moz-selection {
background: #cceae7;
color: #263238;
}
code[class*="language-"]::selection,
pre[class*="language-"]::selection,
code[class*="language-"] ::selection,
pre[class*="language-"] ::selection {
background: #cceae7;
color: #263238;
}
:not(pre) > code[class*="language-"] {
white-space: normal;
border-radius: 0.2em;
padding: 0.1em;
}
pre[class*="language-"] {
overflow: auto;
position: relative;
margin: 0.5em 0;
padding: 1.25em 1em;
}
.language-css > code,
.language-sass > code,
.language-scss > code {
color: #f76d47;
}
[class*="language-"] .namespace {
opacity: 0.7;
}
.token.atrule {
color: #7c4dff;
}
.token.attr-name {
color: #39adb5;
}
.token.attr-value {
color: #f6a434;
}
.token.attribute {
color: #f6a434;
}
.token.boolean {
color: #7c4dff;
}
.token.builtin {
color: #39adb5;
}
.token.cdata {
color: #39adb5;
}
.token.char {
color: #39adb5;
}
.token.class {
color: #39adb5;
}
.token.class-name {
color: #6182b8;
}
.token.comment {
color: #aabfc9;
}
.token.constant {
color: #7c4dff;
}
.token.deleted {
color: #e53935;
}
.token.doctype {
color: #aabfc9;
}
.token.entity {
color: #e53935;
}
.token.function {
color: #7c4dff;
}
.token.hexcode {
color: #f76d47;
}
.token.id {
color: #7c4dff;
font-weight: bold;
}
.token.important {
color: #7c4dff;
font-weight: bold;
}
.token.inserted {
color: #39adb5;
}
.token.keyword {
color: #7c4dff;
}
.token.number {
color: #f76d47;
}
.token.operator {
color: #39adb5;
}
.token.prolog {
color: #aabfc9;
}
.token.property {
color: #39adb5;
}
.token.pseudo-class {
color: #f6a434;
}
.token.pseudo-element {
color: #f6a434;
}
.token.punctuation {
color: #39adb5;
}
.token.regex {
color: #6182b8;
}
.token.selector {
color: #e53935;
}
.token.string {
color: #f6a434;
}
.token.symbol {
color: #7c4dff;
}
.token.tag {
color: #e53935;
}
.token.unit {
color: #f76d47;
}
.token.url {
color: #e53935;
}
.token.variable {
color: #e53935;
}

View File

@ -1,4 +1,5 @@
blockquote {
background: #fafaee;
font-style: italic;
}

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: /

View File

@ -1,55 +1,14 @@
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
import { mdsvex } from 'mdsvex';
import adapter from '@sveltejs/adapter-static';
import fs from 'fs';
import { mdsvex, escapeSvelte } from 'mdsvex';
import remarkAlert from './src/lib/markdown.js'
import relativeImages from 'mdsvex-relative-images'
import remarkToc from 'remark-toc'
import rehypeSlug from 'rehype-slug'
import { getHighlighter } from 'shiki'
import rehypeKatexSvelte from "rehype-katex-svelte";
import remarkMath from 'remark-math'
const contentDir = process.env?.CHRYSOBLOG_CONTENT ?? "src/content";
const metadata = await fs.promises.readFile(contentDir + "/metadata.json")
.then((file) => JSON.parse(file.toString()));
const langs = metadata?.markdown.highlighterLangs ?? [
"javascript",
"lean",
"lisp",
"nix",
"python",
"rust",
"toml",
"typescript",
"yaml",
"c",
"c++",
"sh",
];
const katexOptions = metadata?.markdown.katex ?? {
macros: {
"\\Nat": "\\mathbb N",
"\\Complex": "\\mathbb C",
"\\vec": "\\boldsymbol",
"\\mat": "\\boldsymbol",
"\\rv": "\\mathrm",
},
};
const highlightingThemes = {
light: 'vitesse-light',
dark: 'vitesse-dark',
};
const themes = Object.keys(highlightingThemes).map(function(k){ return highlightingThemes[k] });
const highlighter = await getHighlighter({
themes,
langs,
})
await highlighter.loadLanguage(...langs);
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
@ -61,21 +20,10 @@ const config = {
remarkAlert,
relativeImages,
[remarkToc, {tight: true}],
remarkMath,
],
rehypePlugins: [
[ rehypeKatexSvelte, katexOptions ],
rehypeSlug,
],
highlight: {
highlighter: async (code, lang = 'text') => {
const html = escapeSvelte(highlighter.codeToHtml(code, {
lang,
themes: highlightingThemes,
}))
return `{@html \`${html}\` }`
}
},
]
}),
vitePreprocess(),
],
@ -94,7 +42,9 @@ const config = {
}),
prerender: {
crawl: true,
entries: ['*'],
entries: [
"/page/1/",
],
},
alias: {
$content: contentDir,

View File

@ -109,7 +109,25 @@ const config: Config = {
mono: "'Fira Code','Cascadia Code',Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New','Sarasa Mono SC','Noto Sans CJK SC','monospace', monospace"
},
typography: ((theme) => {
const common = {
const markdownCommon = {
h1: {
color: theme('colors.eucalyptus.700'),
},
h2: {
color: theme('colors.eucalyptus.700'),
},
h3: {
color: theme('colors.eucalyptus.800'),
},
h4: {
color: theme('colors.eucalyptus.800'),
},
a: {
color: theme('colors.eucalyptus.400'),
"&:focus": {
color: theme('colors.java.500'),
},
},
ul: {
listStyleType: "square",
listStylePosition: "outside",
@ -120,75 +138,17 @@ const config: Config = {
},
maxWidth: "100%",
};
const themeTokiwa = {
'--tw-prose-body': theme('colors.gray.900'),
'--tw-prose-headings': theme('colors.eucalyptus.700'),
'--tw-prose-lead': theme('colors.eucalyptus.400'),
'--tw-prose-links': theme('colors.eucalyptus.400'),
'--tw-prose-bold': theme('colors.eucalyptus.800'),
'--tw-prose-counters': theme('colors.gray.900'),
'--tw-prose-bullets': theme('colors.java.900'),
'--tw-prose-hr': theme('colors.gray.600'),
'--tw-prose-quotes': theme('colors.gray.900'),
'--tw-prose-quote-borders': theme('colors.eucalyptus.700'),
'--tw-prose-captions': theme('colors.gray.800'),
'--tw-prose-code': theme('colors.sunglow.900'),
'--tw-prose-pre-code': theme('colors.gray.500'),
'--tw-prose-pre-bg': theme('colors.gray.500'),
'--tw-prose-th-borders': theme('colors.gray.500'),
'--tw-prose-td-borders': theme('colors.gray.500'),
a: {
color: theme('colors.eucalyptus.400'),
"&:hover, &:focus": {
color: theme('colors.java.500'),
},
},
};
const themeStar = {
'--tw-prose-body': theme('colors.gray.400'),
'--tw-prose-headings': theme('colors.eucalyptus.500'),
'--tw-prose-lead': theme('colors.eucalyptus.400'),
'--tw-prose-links': theme('colors.eucalyptus.500'),
'--tw-prose-bold': theme('colors.eucalyptus.100'),
'--tw-prose-counters': theme('colors.gray.100'),
'--tw-prose-bullets': theme('colors.java.100'),
'--tw-prose-hr': theme('colors.gray.400'),
'--tw-prose-quotes': theme('colors.gray.100'),
'--tw-prose-quote-borders': theme('colors.eucalyptus.300'),
'--tw-prose-captions': theme('colors.gray.200'),
'--tw-prose-code': theme('colors.sunglow.300'),
'--tw-prose-pre-code': theme('colors.gray.500'),
'--tw-prose-pre-bg': theme('colors.gray.500'),
'--tw-prose-th-borders': theme('colors.gray.500'),
'--tw-prose-td-borders': theme('colors.gray.500'),
a: {
color: theme('colors.eucalyptus.300'),
"&:hover, &:focus": {
color: theme('colors.java.400'),
},
},
};
return {
DEFAULT: {
css: {
...themeTokiwa,
...common,
},
},
dark: {
css: {
...themeStar,
...common,
...markdownCommon,
},
},
};
}),
}
},
variants: {
extend: { typography: ["dark"] }
},
plugins: [
typography,
],

View File

@ -5,15 +5,3 @@ test('Navigate to blog post', async ({ page }) => {
await page.getByText('The Perfect Math Class').click();
await expect(page).toHaveURL("/post/the-perfect-math-class/");
});
test('Navigate to page 2', async ({ page }) => {
await page.goto('/post');
await page.getByRole('link', { name: 'Next Page' }).click();
await expect(page).toHaveURL("/page/2/");
await page.getByRole('link', { name: 'Prev Page' }).click();
await expect(page).toHaveURL("/post/");
await page.getByRole('link', { name: 'Last Page' }).click();
await expect(page).toHaveURL("/page/2/");
await page.getByRole('link', { name: 'First Page' }).click();
await expect(page).toHaveURL("/post/");
});

View File

@ -1,9 +0,0 @@
import { expect, test } from '@playwright/test';
test('Navigate in series', async ({ page }) => {
await page.goto('/post');
await page.getByText('Placeholder 2').click();
await expect(page).toHaveURL("/post/placeholder2/");
await page.getByText('Placeholder 1').click();
await expect(page).toHaveURL("/post/placeholder1/");
});