update: add headings overview
This commit is contained in:
parent
7f395bcfaa
commit
b529e96a6b
|
@ -1,6 +1,6 @@
|
|||
# blog.hareworks.net
|
||||
|
||||
<img src="https://blog.hareworks.net/img/logo.png" width="200">
|
||||
<img src="./static/img/logo.png" width="200">
|
||||
|
||||
## About
|
||||
|
||||
|
|
1
articles
1
articles
|
@ -1 +0,0 @@
|
|||
Subproject commit 1b15e59b0b8a816cde268dd02a21c5dd85577980
|
4149
package-lock.json
generated
4149
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
48
package.json
48
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "blog.hareworks.net",
|
||||
"version": "0.0.1",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
|
@ -13,32 +13,34 @@
|
|||
"format": "prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.28.1",
|
||||
"@sveltejs/adapter-auto": "^3.0.0",
|
||||
"@sveltejs/adapter-node": "^5.2.2",
|
||||
"@sveltejs/kit": "^2.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.6",
|
||||
"@types/eslint": "^9.6.0",
|
||||
"@types/pg": "^8.11.6",
|
||||
"eslint": "^9.0.0",
|
||||
"@playwright/test": "^1.49.1",
|
||||
"@sveltejs/adapter-auto": "^3.3.1",
|
||||
"@sveltejs/adapter-node": "^5.2.10",
|
||||
"@sveltejs/kit": "^2.12.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.2",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"@types/pg": "^8.11.10",
|
||||
"eslint": "^9.17.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-svelte": "^2.36.0",
|
||||
"globals": "^15.0.0",
|
||||
"prettier": "^3.1.1",
|
||||
"prettier-plugin-svelte": "^3.1.2",
|
||||
"shiki": "^1.16.2",
|
||||
"svelte": "^5.0.0-next.1",
|
||||
"svelte-check": "^3.6.0",
|
||||
"typescript": "^5.0.0",
|
||||
"typescript-eslint": "^8.0.0",
|
||||
"vite": "^5.0.3"
|
||||
"eslint-plugin-svelte": "^2.46.1",
|
||||
"globals": "^15.13.0",
|
||||
"prettier": "^3.4.2",
|
||||
"prettier-plugin-svelte": "^3.3.2",
|
||||
"shiki": "^1.24.2",
|
||||
"svelte": "^5.14.1",
|
||||
"svelte-check": "4.1.1",
|
||||
"typescript": "^5.7.2",
|
||||
"typescript-eslint": "^8.18.1",
|
||||
"vite": "^6.0.3"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"dotenv": "^16.4.5",
|
||||
"dotenv": "^16.4.7",
|
||||
"mdsvex": "^0.12.3",
|
||||
"pg": "^8.12.0",
|
||||
"sass": "^1.77.8",
|
||||
"simple-git": "^3.26.0"
|
||||
"pg": "^8.13.1",
|
||||
"rehype-autolink-headings": "^7.1.0",
|
||||
"rehype-slug": "^6.0.0",
|
||||
"sass": "^1.83.0",
|
||||
"simple-git": "^3.27.0"
|
||||
}
|
||||
}
|
||||
|
|
3127
pnpm-lock.yaml
Normal file
3127
pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
|||
@import './ress.css';
|
||||
@import '../variables.scss';
|
||||
@forward './ress.css';
|
||||
@forward '../variables.scss';
|
||||
|
||||
@font-face {
|
||||
font-family: 'ZenKakuGothicNew-Regular';
|
||||
|
|
|
@ -71,6 +71,37 @@
|
|||
}
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
padding-left: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
&::before {
|
||||
content: '•';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
color: var(--color-concept);
|
||||
}
|
||||
}
|
||||
ul > li {
|
||||
&::before {
|
||||
content: '•';
|
||||
}
|
||||
}
|
||||
ol > li {
|
||||
counter-increment: item;
|
||||
&::before {
|
||||
content: counter(item) '.';
|
||||
}
|
||||
}
|
||||
|
||||
blockquote {
|
||||
position: relative;
|
||||
font-size: 1.1rem;
|
||||
|
|
67
src/lib/components/overview.svelte
Normal file
67
src/lib/components/overview.svelte
Normal file
|
@ -0,0 +1,67 @@
|
|||
<script lang="ts">
|
||||
export let html: string;
|
||||
|
||||
const headings: {
|
||||
level: number;
|
||||
id: string;
|
||||
text: string;
|
||||
}[] = [];
|
||||
html.match(/<h(\d) id="([^"]+)">([^<]+)<\/h\d>/g)?.forEach((match) => {
|
||||
console.log(match);
|
||||
const [, level, id, text] = match.match(/<h(\d) id="([^"]+)">([^<]+)<\/h\d>/)!;
|
||||
headings.push({ level: +level, id, text });
|
||||
});
|
||||
let output = '';
|
||||
let lastLevel = 1;
|
||||
headings.forEach(({ level, id, text }) => {
|
||||
console.log(level, id, text);
|
||||
if (level > lastLevel) {
|
||||
output += '<ul>';
|
||||
} else if (level < lastLevel) {
|
||||
output += '</li></ul>'.repeat(lastLevel - level);
|
||||
} else {
|
||||
if (output !== '') {
|
||||
}
|
||||
}
|
||||
output += `<li><a href="#${id}">${text}</a>`;
|
||||
lastLevel = level;
|
||||
});
|
||||
output += '</li>'.repeat(lastLevel);
|
||||
</script>
|
||||
|
||||
<div class="overview">
|
||||
<ul>
|
||||
{@html output}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.overview {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
:global(ul) {
|
||||
list-style-type: none;
|
||||
margin-left: 0.5em;
|
||||
padding-left: 0.25em;
|
||||
border-left: 1px solid var(--line-primary);
|
||||
}
|
||||
ul {
|
||||
border-left: none;
|
||||
}
|
||||
:global(li) {
|
||||
padding: 0.5em 0.5em 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
:global(a) {
|
||||
text-decoration: none;
|
||||
color: var(--text-primary);
|
||||
padding: 0.15em 0.4em;
|
||||
border-radius: 0.25em;
|
||||
text-wrap: nowrap;
|
||||
box-sizing: border-box;
|
||||
&:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -5,6 +5,7 @@
|
|||
import Footer from '$lib/components/footer.svelte';
|
||||
import * as publish from '$lib/publish';
|
||||
import FormattedDate from '$lib/components/formatted_date.svelte';
|
||||
import Overview from '$lib/components/overview.svelte';
|
||||
|
||||
import type { Article } from '$lib/article';
|
||||
export let data: Article;
|
||||
|
@ -32,39 +33,46 @@
|
|||
</div> -->
|
||||
<div class="container">
|
||||
<main>
|
||||
<div class="title">
|
||||
<h1>{data.title}</h1>
|
||||
<div class="meta">
|
||||
<div class="left">
|
||||
<span>
|
||||
released <FormattedDate date={data.released_at} />
|
||||
{#if isUpdated}<br />
|
||||
updated <FormattedDate date={data.updated_at} />
|
||||
{/if}
|
||||
</span>
|
||||
<span>
|
||||
<a href="/category/{data.category}"
|
||||
>{data.category[0].toUpperCase() + data.category.slice(1)}</a
|
||||
>
|
||||
{#each data.tags as tag}
|
||||
<a href={`/search?tag=${tag}`}>{tag}</a>
|
||||
{/each}
|
||||
</span>
|
||||
<div class="content">
|
||||
<div class="title">
|
||||
<h1>{data.title}</h1>
|
||||
<div class="meta">
|
||||
<div class="left">
|
||||
<span>
|
||||
released <FormattedDate date={data.released_at} />
|
||||
{#if isUpdated}<br />
|
||||
updated <FormattedDate date={data.updated_at} />
|
||||
{/if}
|
||||
</span>
|
||||
<span>
|
||||
<a href="/category/{data.category}"
|
||||
>{data.category[0].toUpperCase() + data.category.slice(1)}</a
|
||||
>
|
||||
{#each data.tags as tag}
|
||||
<a href={`/search?tag=${tag}`}>{tag}</a>
|
||||
{/each}
|
||||
</span>
|
||||
</div>
|
||||
<div class="right">
|
||||
<img
|
||||
src="https://gitea.hareworks.net/avatars/e595ec10f8052671deab92caca78220fc838f63259bcd542a0d93f53e2371d52?size=512"
|
||||
alt="Author"
|
||||
/>
|
||||
<div>@{data.author}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<img
|
||||
src="https://gitea.hareworks.net/avatars/e595ec10f8052671deab92caca78220fc838f63259bcd542a0d93f53e2371d52?size=512"
|
||||
alt="Author"
|
||||
/>
|
||||
<div>@{data.author}</div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<div class="document">
|
||||
<slot />
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<div class="document">
|
||||
<slot />
|
||||
<div class="card-container">
|
||||
<div class="card">
|
||||
<Overview html={data.content} />
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
@ -100,25 +108,43 @@
|
|||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
.card {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
main {
|
||||
border: 1px solid var(--line-primary);
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
box-sizing: border-box;
|
||||
color: white;
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: start;
|
||||
.content {
|
||||
border: 1px solid var(--line-primary);
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
min-height: 100%;
|
||||
padding: 20px;
|
||||
padding-top: 100px;
|
||||
max-width: 800px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.card-container {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
overflow-y: auto;
|
||||
background-color: var(--background-primary);
|
||||
box-sizing: border-box;
|
||||
padding: 20px;
|
||||
padding-top: 100px;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
.card {
|
||||
width: 300px;
|
||||
padding: 0.5em 0;
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
import { compile, escapeSvelte } from "mdsvex";
|
||||
import { createHighlighter } from "shiki";
|
||||
import { kanagawa_dark as theme } from "$lib/kanagawa"
|
||||
|
||||
import rehypeAutolinkHeadings from "rehype-autolink-headings";
|
||||
import rehypeSlug from "rehype-slug";
|
||||
|
||||
const highlighter = await createHighlighter({
|
||||
themes: [theme],
|
||||
|
@ -19,5 +20,6 @@ export default async function compileArticle(code: string) {
|
|||
return html;
|
||||
},
|
||||
},
|
||||
rehypePlugins: [rehypeSlug],
|
||||
});
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import type { Postgres } from '$lib/server/database/postgres';
|
||||
import { type SimpleGit, CheckRepoActions } from 'simple-git';
|
||||
import fs from 'fs';
|
||||
import compile from '$lib/server/article_compiler';
|
||||
import compileArticle from '$lib/server/article_compiler';
|
||||
import type { TableSchema } from '$lib/server/database/table';
|
||||
import server_table from '$lib/server/database/table';
|
||||
import { format } from '$lib/scripts/formatted_date';
|
||||
|
@ -33,7 +33,7 @@ const load = async (db: Postgres, git: SimpleGit) => {
|
|||
console.log(`Author: ${author} <${email}>\nReleased at: ${format(released_at)}\nUpdated at: ${format(updated_at)}`);
|
||||
|
||||
const category = path.split('/')[3];
|
||||
const compiled = await compile(fs.readFileSync(path, 'utf-8'));
|
||||
const compiled = await compileArticle(fs.readFileSync(path, 'utf-8'));
|
||||
const title = compiled.data.fm.title;
|
||||
const tags: string[] = compiled.data.fm.tags;
|
||||
const image = compiled.data.fm.image;
|
||||
|
|
|
@ -3,14 +3,9 @@ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
|||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
|
||||
// for more information about preprocessors
|
||||
preprocess: vitePreprocess(),
|
||||
|
||||
kit: {
|
||||
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
||||
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
|
||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||
adapter: adapter()
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue
Block a user