Compare commits

...
74 Commits
Author SHA1 Message Date
Hare 912e1c46d9 add: search articles api endpoint 2025-02-05 00:06:18 +09:00
Hare 3e32df5761 chore: optimize Dockerfile for pnpm usage and build process 2024-12-18 07:14:14 +09:00
Hare 992c99d2fb update: refine layout and spacing in blog styles and article page 2024-12-18 07:14:01 +09:00
Hare 5c05db4580 update: layout styles in overview and article pages 2024-12-18 06:34:55 +09:00
Hare bafa8e4d18 change: add rehypeAutolinkHeadings plugin to article compiler 2024-12-18 06:07:46 +09:00
Hare b529e96a6b update: add headings overview 2024-12-18 06:02:28 +09:00
Hare 7f395bcfaa Merge branch 'master' into develop 2024-10-23 21:39:13 +09:00
Hare 97600ed402 change: svelte default to temp logo 2024-10-23 21:38:33 +09:00
Hare ae48a85ece change: discord invite link 2024-10-23 20:17:39 +09:00
Hare 9f162e83c4 Merge pull request 'fix: Clone and analyze the repository correctly' (#15) from develop into master
Reviewed-on: #15
2024-09-27 00:49:42 +09:00
Hare e4548bf10b Merge branch 'master' into develop 2024-09-27 00:49:35 +09:00
Hare 5b441ff0cf fix: Clone and analyze the repository correctly 2024-09-27 00:48:22 +09:00
Hare 76bd2a5e1b Merge pull request 'develop' (#14) from develop into master
Reviewed-on: #14
2024-09-27 00:17:58 +09:00
Hare e3a83ee561 Merge branch 'master' into develop 2024-09-27 00:17:51 +09:00
Hare dd057dd335 Refactor: Update imports for server modules 2024-09-27 00:16:57 +09:00
Hare f11bdf15fc fix typo #2 2024-09-26 23:44:12 +09:00
Hare 5ec1839ec7 fix typo 2024-09-26 23:43:51 +09:00
Hare 2fdcc1633d Merge pull request 'fix: initializing git during build' (#13) from develop into master
Reviewed-on: #13
2024-09-26 23:37:15 +09:00
Hare 6173dffba3 fix: initializing git during build 2024-09-26 23:36:12 +09:00
Hare e389a3d2f9 Merge branch 'develop' 2024-09-26 23:12:09 +09:00
Hare 4e2422fa09 Merge pull request 'feat-google-analytics' (#11) from feat-google-analytics into develop
Reviewed-on: #11
2024-09-26 23:07:52 +09:00
Hare 8d09641e5d Remove articles subproject 2024-09-26 23:06:41 +09:00
Hare 2b6e3bfa9f feat: Add Google Analytics tracking code to layout component 2024-09-26 23:05:42 +09:00
Hare 3797e33450 Merge branch 'develop' of gitea.hareworks.net:Hare/blog.hareworks.net into develop 2024-09-26 22:59:34 +09:00
Hare db831b0683 Merge pull request 'feat-git' (#10) from feat-git into master
Reviewed-on: #10
2024-09-25 23:30:08 +09:00
Hare c08bb9dcef Merge branch 'master' into feat-git 2024-09-25 23:29:55 +09:00
Hare 36237f0708 feat: apply author and email fields to Articles 2024-09-25 23:29:21 +09:00
Hare 3145073381 feat: add a column to store datafrom git 2024-09-21 14:51:15 +09:00
Hare 7032c552a8 feat: Refactor formatted_date component and add format function 2024-09-18 17:32:02 +09:00
Hare 121cb60a5f feat: Add ress.css for global CSS reset 2024-09-18 17:31:39 +09:00
Hare 7cd8db36e2 feat: Add simple-git dependency 2024-09-18 17:31:06 +09:00
Hare e251697e92 Update blog logo and URL in README.md 2024-09-18 14:54:07 +09:00
Hare 58ce0f28c7 Merge pull request 'feat: codeblock highlighter and styling' (#9) from develop into master
Reviewed-on: #9
2024-09-07 02:49:35 +09:00
Hare 1637776b12 Merge branch 'master' into develop 2024-09-07 02:49:23 +09:00
Hare 4a7960f183 fix: always updated all articles 2024-09-07 02:48:22 +09:00
Hare 005fd864b2 feat: change article load logic
- change highlighter prism to shiki
- separate logic into several files
2024-09-07 02:42:50 +09:00
Hare 68dde60151 feat: add codeblock style 2024-09-07 02:38:47 +09:00
Hare 0512b64ce4 feat: add kanagawa theme 2024-09-07 02:34:16 +09:00
Hare 8794fff8b2 chore: npm i shiki 2024-09-07 02:19:29 +09:00
Hare dece9dfb79 fix: delete non working codeblock component 2024-09-04 20:20:57 +09:00
Hare 7c600da4f5 fix: remove file extension from uri 2024-09-04 19:37:06 +09:00
Hare 19d63ed0d5 Merge pull request 'style tweaks' (#8) from develop into master
Reviewed-on: #8
2024-09-03 21:37:03 +09:00
Hare 11210336cd feat: git clone if articles does not exist 2024-09-03 21:34:56 +09:00
Hare 8b599165e1 feat: pre-tag text warp did not work. 2024-09-03 18:27:49 +09:00
Hare b8eb9acb8b chore: Remove articles subproject 2024-09-03 18:24:54 +09:00
Hare 4c87679fbe feat: move database initialization to get_connection.ts 2024-09-03 09:06:51 +09:00
Hare 0398b95c24 feat: Changed to not use getConnection function directly from the page. 2024-09-03 09:06:21 +09:00
Hare 632521e7f4 chore: Update article page to hide updated date if not edited 2024-09-03 08:20:09 +09:00
Hare 1670969d4f chore: Remove unused Head component from article page 2024-09-03 07:46:56 +09:00
Hare 0e06457894 feat: use hr instead od h1 border 2024-09-03 07:25:14 +09:00
Hare 41674ea6a1 Merge pull request 'feat: change article managment: git submodules to dynamic git clone&pull' (#7) from develop into master
Reviewed-on: #7
2024-09-03 07:01:14 +09:00
Hare d7483f0f00 Merge branch 'develop' of gitea.hareworks.net:Hare/blog.hareworks.net into develop 2024-09-03 06:58:15 +09:00
Hare 04f9bd5e99 chore: Update git command to use custom SSH key for pulling articles 2024-09-03 06:58:13 +09:00
Hare 8a5d09d615 chore: Update Dockerfile to clone blog articles repository 2024-09-03 06:52:49 +09:00
Hare 700304b466 chore: Add key files to .gitignore 2024-09-03 06:52:35 +09:00
Hare 126e748955 chore: Remove articles subproject 2024-09-03 06:04:28 +09:00
Hare 78d733ace6 Merge pull request 'chore: fix git install target from builder to runner' (#6) from develop into master
Reviewed-on: #6
2024-09-03 05:24:49 +09:00
Hare c728dd5dc1 Merge branch 'master' into develop 2024-09-03 05:24:40 +09:00
Hare f332b7e27e chore: fix git install target from builder to runner 2024-09-03 05:24:17 +09:00
Hare e7a6739882 Merge pull request 'chore: Install git in Docker image for building the app' (#5) from develop into master
Reviewed-on: #5
2024-09-03 05:19:20 +09:00
Hare 119686d898 Merge branch 'master' into develop 2024-09-03 05:19:07 +09:00
Hare 96610e2b66 chore: Install git in Docker image for building the app 2024-09-03 05:18:39 +09:00
Hare 9b7d5da439 Merge pull request 'article update automation' (#4) from develop into master
Reviewed-on: #4
2024-09-03 05:14:11 +09:00
Hare 49e4646375 Merge branch 'develop' of gitea.hareworks.net:Hare/blog.hareworks.net into develop 2024-09-03 04:47:46 +09:00
Hare 6c619cc019 feat: Initialize database connection in fetch_articles server route 2024-09-03 04:46:20 +09:00
Hare 0da14f3ebf feat: pull articles when database initialization 2024-09-03 04:45:42 +09:00
Hare bc0b59042e chore: Add fetch_articles server route 2024-09-02 22:02:30 +09:00
Hare 4fb912cbf9 fix: use hook data to create a new DB instance 2024-09-02 22:02:19 +09:00
Hare 5c01942948 feat: Update pg type 2024-09-02 21:48:59 +09:00
Hare cdec7e78b7 Update subproject 2024-09-02 18:35:11 +09:00
Hare 30744e3df1 Merge pull request 'adjustments to behavior and specifications' (#3) from develop into master
Reviewed-on: #3
2024-08-28 04:40:16 +09:00
Hare 3721b3bcd6 Merge branch 'master' into develop 2024-08-28 04:40:05 +09:00
Hare 2cecf023d2 Merge pull request 'make it buildable to db system' (#2) from develop into master
Reviewed-on: #2
2024-08-28 00:15:50 +09:00
Hare 80535f1723 Merge pull request 'It is now possible to display' (#1) from develop into master
Reviewed-on: #1
2024-08-26 18:55:09 +09:00
38 changed files with 4964 additions and 4598 deletions
+3
View File
@@ -1,6 +1,9 @@
node_modules node_modules
postgres postgres
key
key.pub
# Output # Output
.output .output
.vercel .vercel
-3
View File
@@ -1,3 +0,0 @@
[submodule "articles"]
path = articles
url = git@gitea.hareworks.net:Hare/blog-articles.git
+6 -33
View File
@@ -1,38 +1,11 @@
# create-svelte # blog.hareworks.net
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte). <img src="./static/img/logo.png" width="200">
## Creating a project ## About
If you're seeing this, you've probably already done this step. Congrats! This is the source code for my blog at [blog.hareworks.net](https://blog.hareworks.net).
```bash ## Made with SvelteKit
# create a new project in the current directory
npm create svelte@latest
# create a new project in my-app This blog is made with SvelteKit.
npm create svelte@latest my-app
```
## Developing
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
```bash
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
## Building
To create a production version of your app:
```bash
npm run build
```
You can preview the production build with `npm run preview`.
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
Submodule articles deleted from 172da3590a
+16 -7
View File
@@ -1,23 +1,32 @@
# For Build # For Build
FROM node:22-slim as builder FROM node:22-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
WORKDIR /app WORKDIR /app
COPY package.json ./ COPY package.json ./
COPY package-lock.json ./ COPY pnpm-lock.yaml ./
COPY tsconfig.json ./ COPY tsconfig.json ./
RUN npm ci
FROM base AS builder
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
COPY . . COPY . .
RUN pnpm run build
RUN npm run build
# For Run FROM base
FROM node:22-slim RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile
WORKDIR /app WORKDIR /app
COPY ./articles ./articles COPY ./key ./key
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* && \
mkdir -p ~/.ssh && \
ssh-keyscan -t rsa gitea.hareworks.net >> ~/.ssh/known_hosts && \
git -c core.sshCommand="ssh -i ./key -F /dev/null" clone git@gitea.hareworks.net:Hare/blog-articles.git articles
COPY --from=builder /app/build ./build COPY --from=builder /app/build ./build
COPY --from=builder /app/package.json . COPY --from=builder /app/package.json .
-4079
View File
File diff suppressed because it is too large Load Diff
+25 -21
View File
@@ -1,6 +1,6 @@
{ {
"name": "blog.hareworks.net", "name": "blog.hareworks.net",
"version": "0.0.1", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite dev", "dev": "vite dev",
@@ -13,30 +13,34 @@
"format": "prettier --write ." "format": "prettier --write ."
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.28.1", "@playwright/test": "^1.49.1",
"@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-auto": "^3.3.1",
"@sveltejs/adapter-node": "^5.2.2", "@sveltejs/adapter-node": "^5.2.10",
"@sveltejs/kit": "^2.0.0", "@sveltejs/kit": "^2.12.1",
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.6", "@sveltejs/vite-plugin-svelte": "^5.0.2",
"@types/eslint": "^9.6.0", "@types/eslint": "^9.6.1",
"@types/pg": "^8.11.6", "@types/pg": "^8.11.10",
"eslint": "^9.0.0", "eslint": "^9.17.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.36.0", "eslint-plugin-svelte": "^2.46.1",
"globals": "^15.0.0", "globals": "^15.13.0",
"prettier": "^3.1.1", "prettier": "^3.4.2",
"prettier-plugin-svelte": "^3.1.2", "prettier-plugin-svelte": "^3.3.2",
"svelte": "^5.0.0-next.1", "shiki": "^1.24.2",
"svelte-check": "^3.6.0", "svelte": "^5.14.1",
"typescript": "^5.0.0", "svelte-check": "4.1.1",
"typescript-eslint": "^8.0.0", "typescript": "^5.7.2",
"vite": "^5.0.3" "typescript-eslint": "^8.18.1",
"vite": "^6.0.3"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"dotenv": "^16.4.5", "dotenv": "^16.4.7",
"mdsvex": "^0.12.3", "mdsvex": "^0.12.3",
"pg": "^8.12.0", "pg": "^8.13.1",
"sass": "^1.77.8" "rehype-autolink-headings": "^7.1.0",
"rehype-slug": "^6.0.0",
"sass": "^1.83.0",
"simple-git": "^3.27.0"
} }
} }
+3127
View File
File diff suppressed because it is too large Load Diff
+5 -3
View File
@@ -1,13 +1,15 @@
// See https://kit.svelte.dev/docs/types#app // See https://kit.svelte.dev/docs/types#app
// for information about these interfaces // for information about these interfaces
import { Database } from 'pg'; import { Pool } from "pg";
import type { SimpleGit } from "simple-git";
declare global { declare global {
namespace App { namespace App {
// interface Error {} // interface Error {}
interface Locals { interface Locals {
db: any; db: Pool;
} git: SimpleGit;
}
// interface PageData {} // interface PageData {}
// interface PageState {} // interface PageState {}
// interface Platform {} // interface Platform {}
+4 -2
View File
@@ -1,9 +1,11 @@
import type { Handle } from '@sveltejs/kit' import type { Handle } from '@sveltejs/kit'
import { getConnection } from '$lib/server/database/get_connection' import { getPool } from '$lib/server/database/pool';
import git from '$lib/server/git';
export const handle: Handle = async ({ event, resolve }) => { export const handle: Handle = async ({ event, resolve }) => {
const pg = getConnection(); const pg = getPool;
event.locals.db = pg; event.locals.db = pg;
event.locals.git = git;
const result = await resolve(event); const result = await resolve(event);
return result; return result;
+4 -3
View File
@@ -1,4 +1,5 @@
@import '../variables.scss'; @forward './ress.css';
@forward '../variables.scss';
@font-face { @font-face {
font-family: 'ZenKakuGothicNew-Regular'; font-family: 'ZenKakuGothicNew-Regular';
@@ -6,7 +7,7 @@
} }
:root { :root {
font-size: 16px; font-size: 18px;
} }
body { body {
@@ -15,5 +16,5 @@ body {
color: #ffffff; color: #ffffff;
font-family: 'ZenKakuGothicNew-Regular', '游ゴシック体', 'Yu Gothic', YuGothic, 'ヒラギノ角ゴシック Pro', font-family: 'ZenKakuGothicNew-Regular', '游ゴシック体', 'Yu Gothic', YuGothic, 'ヒラギノ角ゴシック Pro',
'Hiragino Kaku Gothic Pro', 'メイリオ', Meiryo, Osaka, 'MS PGothic', sans-serif; 'Hiragino Kaku Gothic Pro', 'メイリオ', Meiryo, Osaka, 'MS PGothic', sans-serif;
font-size: 16px; font-size: 1rem;
} }
+2
View File
@@ -7,6 +7,8 @@ export type Article = {
category: string; category: string;
released_at: Date; released_at: Date;
updated_at: Date; updated_at: Date;
author: string;
email: string;
tags: string[]; tags: string[];
image: string; image: string;
publish: Publish; publish: Publish;
+274 -172
View File
@@ -1,174 +1,276 @@
.document { .document {
--color-text: #ffffff; --color-text: #ffffff;
--color-concept: hsl(39, 100%, 25%, 0.5); --color-concept: hsla(40, 100%, 90%, 0.5);
--color-concept-hsl: 39, 100%, 25%; --color-concept-hsl: 39, 100%, 25%;
--color-outline: #ffffff40; --color-outline: #ffffff40;
* { width: 100%;
color: var(--color-text);
} color: var(--color-text);
h1 {
position: relative; padding-left: 0rem;
width: auto; box-sizing: border-box;
font-size: 2rem;
border-bottom: 1px solid var(--color-outline); h1 {
padding-bottom: 0.5rem; position: relative;
} width: auto;
> *:not(h1) { font-size: 1.8rem;
margin-left: 2rem; padding: 1.5rem 0 0.25rem 0;
} margin: 0 0 0.75rem 0rem;
h2 { border-bottom: 1px solid var(--color-outline);
font-size: 1.5rem; }
position: relative;
&::before { hr {
content: '#'; padding: 0;
position: absolute; margin: 0;
top: 0; border: none;
left: -1.5rem; border-top: 1px solid var(--color-outline);
font-size: 1.75rem; margin-left: -1rem;
color: rgb(131, 131, 131); }
}
} h2 {
h3 { padding: 1rem 0 0.25rem 0;
font-size: 1.25rem; font-size: 1.5rem;
} position: relative;
h4 {
font-size: 1.05rem; // &::before {
} // content: '#';
hr { // position: absolute;
border: none; // top: 0;
border-top: 1px solid var(--color-outline); // left: -1.5rem;
} // font-size: 1.75rem;
img { // color: rgb(131, 131, 131);
max-height: 300px; // }
} }
a {
position: relative; h3 {
display: inline-block; font-size: 1.25rem;
transform: translate(0, 0); }
text-shadow: 0 0 1rem hsl(var(--color-concept-hsl));
transition: 0.2s ease-out; h4 {
font-weight: normal; font-size: 1.05rem;
&:hover { }
transform: translate(0, -0.1rem);
} hr {
} border: none;
blockquote { border-top: 1px solid var(--color-outline);
position: relative; }
font-size: 1.1rem;
font-weight: bold; img {
padding: 0; max-height: 300px;
padding-left: 1rem; }
color: rgb(162, 162, 162);
&::before { a {
content: ''; color: var(--color-text);
position: absolute; position: relative;
top: 0; display: inline-block;
left: -0.5rem; transform: translate(0, 0);
font-size: 2rem; text-shadow: 0 0 1rem hsl(var(--color-concept-hsl));
color: rgb(131, 131, 131); transition: 0.2s ease-out;
} font-weight: normal;
}
warn { &:hover {
position: relative; transform: translate(0, -0.1rem);
display: block; }
padding: 1rem 0.7rem 0.7rem; }
background-image: linear-gradient(to right, var(--color-outline), transparent 75%);
background-origin: border-box; ul,
box-shadow: inset 0 0 0 100vh var(--background-color); ol {
text-shadow: 0 0 1rem hsl(var(--color-concept-hsl),1); list-style: none;
border: 1px solid transparent; padding: 0;
border-radius: 0.5rem; margin: 0;
&::before { }
content: 'note:warn';
position: relative; li {
display: block; position: relative;
top: 0; padding-left: 1rem;
font-size: 0.8rem; margin-bottom: 0.5rem;
margin-bottom: -0.3rem; &::before {
transform: translate(0, -0.5rem); content: '';
text-shadow: 0 0 1rem red; position: absolute;
} top: 0;
} left: 0;
pre { color: var(--color-concept);
font-family: monospace; }
margin-top: 0.75rem; }
margin-bottom: 0.75rem; ul > li {
} &::before {
code { content: '';
display: inline-block; }
padding: 0.2rem; }
background: rgba(255, 255, 255, 0.25); ol > li {
border-radius: 0.2rem; counter-increment: item;
font-family: monospace; &::before {
} content: counter(item) '.';
details { }
position: relative; }
display: block;
border-radius: 0.5rem; blockquote {
transition: 0.2s ease-out; position: relative;
transition-property: height; font-size: 1.1rem;
padding-inline-start: 0.1rem; font-weight: bold;
> summary { padding: 0;
position: relative; padding-left: 1rem;
border-radius: 0.5rem; color: rgb(162, 162, 162);
list-style: ' ' outside;
margin-left: 1rem; &::before {
padding: 0.2rem; content: '';
background: linear-gradient(to right, rgba(255, 255, 255, 0.25), transparent 200px); position: absolute;
cursor: pointer; top: 0;
&:hover { left: -0.5rem;
text-shadow: 0 0 0.25rem rgb(255, 255, 255); font-size: 2rem;
} color: rgb(131, 131, 131);
} }
> p { }
margin-block-start: 0.5rem;
margin-block-end: 0.5rem; warn {
margin-left: 1rem; position: relative;
display: none; display: block;
} padding: 1rem 0.7rem 0.7rem;
&[open] { background-image: linear-gradient(to right, var(--color-outline), transparent 75%);
> summary { background-origin: border-box;
list-style: ' ' outside; box-shadow: inset 0 0 0 100vh var(--background-color);
} text-shadow: 0 0 1rem hsl(var(--color-concept-hsl), 1);
> p { border: 1px solid transparent;
animation: detailsIn 0.5s ease; border-radius: 0.5rem;
display: block;
} &::before {
} content: 'note:warn';
} position: relative;
@keyframes detailsIn { display: block;
0% { top: 0;
opacity: 0; font-size: 0.8rem;
transform: translateY(-10px); margin-bottom: -0.3rem;
} transform: translate(0, -0.5rem);
100% { text-shadow: 0 0 1rem red;
opacity: 1; }
transform: none; }
}
} code:not(pre > code) {
table { display: inline-block;
border-spacing: 0; font-family: monospace;
border: none; font-size: 0.9rem;
border: 1px solid var(--color-outline); background: #66666666;
border-radius: 10px; padding: 0 0.3rem;
overflow: hidden; border-radius: 0.5rem;
box-shadow: 0 0 0.5rem hsl(var(--color-concept-hsl), 0.5); }
thead {
background-color: rgba(0, 0, 0, 0.1); pre {
color: #fff; font-family: monospace;
} position: relative;
tr { display: block;
&:nth-child(2n) { margin-top: 0.75rem;
background-color: #ffffff0d; margin-bottom: 0.75rem;
} // border: 2px solid #ffffff40;
} border-radius: 0.5rem;
td, overflow-x: auto;
th { font-size: 0.9rem;
padding: 0.5em;
border-left: 1px solid var(--color-outline); &::-webkit-scrollbar {
&:first-child { border-radius: 10px;
border-left: none; height: 10px;
} width: 8px;
} background: #3d3d3d;
} }
&::-webkit-scrollbar-thumb {
background: #6f6f6f;
border-radius: 10px;
}
&::-webkit-scrollbar-track {
border-radius: 10px;
}
> code {
display: inline-block;
padding: 0.5rem;
border-radius: 0.6rem;
font-family: monospace;
box-sizing: border-box;
min-width: 100%;
}
}
details {
position: relative;
display: block;
border-radius: 0.5rem;
transition: 0.2s ease-out;
transition-property: height;
padding-inline-start: 0.1rem;
> summary {
position: relative;
border-radius: 0.5rem;
list-style: ' ' outside;
margin-left: 1rem;
padding: 0.2rem;
background: linear-gradient(to right, rgba(255, 255, 255, 0.25), transparent 200px);
cursor: pointer;
&:hover {
text-shadow: 0 0 0.25rem rgb(255, 255, 255);
}
}
> p {
margin-block-start: 0.5rem;
margin-block-end: 0.5rem;
margin-left: 1rem;
display: none;
}
&[open] {
> summary {
list-style: ' ' outside;
}
> p {
animation: detailsIn 0.5s ease;
display: block;
}
}
}
@keyframes detailsIn {
0% {
opacity: 0;
transform: translateY(-10px);
}
100% {
opacity: 1;
transform: none;
}
}
table {
border-spacing: 0;
border: none;
border: 1px solid var(--color-outline);
border-radius: 10px;
overflow: hidden;
box-shadow: 0 0 0.5rem hsl(var(--color-concept-hsl), 0.5);
thead {
background-color: rgba(0, 0, 0, 0.1);
color: #fff;
}
tr {
&:nth-child(2n) {
background-color: #ffffff0d;
}
}
td,
th {
padding: 0.5em;
border-left: 1px solid var(--color-outline);
&:first-child {
border-left: none;
}
}
}
} }
+2 -15
View File
@@ -3,22 +3,9 @@
if (typeof date === 'string') date = new Date(date); if (typeof date === 'string') date = new Date(date);
const pad = function (str: string): string { import { format } from "$lib/scripts/formatted_date";
return ('0' + str).slice(-2);
};
const year = date.getFullYear().toString(); const formattedDate = format(date);
const month = pad((date.getMonth() + 1).toString());
const day = pad(date.getDate().toString());
const hour = pad(date.getHours().toString());
const min = pad(date.getMinutes().toString());
const sec = pad(date.getSeconds().toString());
const tz = -date.getTimezoneOffset();
const sign = tz >= 0 ? '+' : '-';
const tzHour = pad((tz / 60).toString());
const tzMin = pad((tz % 60).toString());
let formattedDate = `${year}-${month}-${day}T${hour}:${min}:${sec}${sign}${tzHour}:${tzMin}`;
</script> </script>
{formattedDate} {formattedDate}
+68
View File
@@ -0,0 +1,68 @@
<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);
box-sizing: border-box;
}
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>
+915
View File
@@ -0,0 +1,915 @@
{
"name": "Kanagawa",
"type": "dark",
"semanticHighlighting": true,
"colors": {
"activityBar.background": "#2A2A37",
"activityBar.foreground": "#DCD7BA",
"activityBarBadge.background": "#658594",
"activityBarBadge.foreground": "#DCD7BA",
"badge.background": "#2A2A37",
"button.background": "#2A2A37",
"button.secondaryBackground": "#223249",
"button.secondaryForeground": "#DCD7BA",
"checkbox.border": "#223249",
"debugToolBar.background": "#16161D",
"descriptionForeground": "#DCD7BA",
"diffEditor.insertedTextBackground": "#2B3328",
"dropdown.background": "#16161D",
"dropdown.border": "#16161D",
"editor.background": "#1F1F28",
"editor.findMatchBackground": "#2D4F67",
"editor.findMatchBorder": "#FF9E3B",
"editor.findMatchHighlightBackground": "#2D4F67",
"editor.foreground": "#DCD7BA",
"editorHoverWidget.highlightForeground": "#658594",
"editorInlayHint.foreground": "#727169",
"editorInlayHint.background": "#1F1F28",
"editor.lineHighlightBackground": "#2A2A37",
"editorLineNumber.activeForeground": "#957FB8",
"editorGutter.addedBackground": "#76946A",
"editorGutter.deletedBackground": "#C34043",
"editorGutter.modifiedBackground": "#DCA561",
"editor.wordHighlightBackground": "#3636464d",
"editor.wordHighlightBorder": "#54546D",
"editor.wordHighlightStrongBackground": "#3636464d",
"editor.wordHighlightStrongBorder": "#54546D",
"editor.selectionBackground": "#223249",
"editor.selectionHighlightBackground": "#363646",
"editor.selectionHighlightBorder": "#54546D",
"editorBracketMatch.background": "#16161D",
"editorBracketMatch.border": "#54546D",
"editorBracketHighlight.foreground1": "#957FB8",
"editorBracketHighlight.foreground2": "#FFA066",
"editorBracketHighlight.foreground3": "#7E9CD8",
"editorBracketHighlight.foreground4": "#D27E99",
"editorBracketHighlight.foreground5": "#E6C384",
"editorBracketHighlight.foreground6": "#7AA89F",
"editorBracketHighlight.unexpectedBracket.foreground": "#FF5D62",
"editorBracketPairGuide.activeBackground1": "#957FB8",
"editorBracketPairGuide.activeBackground2": "#FFA066",
"editorBracketPairGuide.activeBackground3": "#7E9CD8",
"editorBracketPairGuide.activeBackground4": "#D27E99",
"editorBracketPairGuide.activeBackground5": "#E6C384",
"editorBracketPairGuide.activeBackground6": "#7AA89F",
"editorCursor.background": "#DCD7BA",
"editorCursor.foreground": "#DCD7BA",
"editorError.foreground": "#E82424",
"editorGroup.border": "#16161D",
"editorGroupHeader.tabsBackground": "#16161D",
"editorHoverWidget.background": "#1F1F28",
"editorHoverWidget.border": "#2A2A37",
"editorIndentGuide.activeBackground": "#363646",
"editorIndentGuide.background": "#2A2A37",
"editorLineNumber.foreground": "#54546D",
"editorMarkerNavigation.background": "#363646",
"editorRuler.foreground": "#363646",
"editorSuggestWidget.background": "#223249",
"editorSuggestWidget.border": "#223249",
"editorSuggestWidget.selectedBackground": "#2D4F67",
"editorWarning.foreground": "#FF9E3B",
"editorWhitespace.foreground": "#1F1F28",
"editorWidget.background": "#1f1f28",
"focusBorder": "#223249",
"gitDecoration.ignoredResourceForeground": "#727169",
"input.background": "#16161D",
"list.activeSelectionBackground": "#363646",
"list.activeSelectionForeground": "#DCD7BA",
"list.focusBackground": "#2A2A37",
"list.focusForeground": "#DCD7BA",
"list.highlightForeground": "#7E9CD8",
"list.hoverBackground": "#363646",
"list.hoverForeground": "#DCD7BA",
"list.inactiveSelectionBackground": "#2A2A37",
"list.inactiveSelectionForeground": "#DCD7BA",
"list.warningForeground": "#FF9E3B",
"menu.foreground": "#DCD7BA",
"menu.separatorBackground": "#16161D",
"minimapGutter.addedBackground": "#76946A",
"minimapGutter.deletedBackground": "#C34043",
"minimapGutter.modifiedBackground": "#DCA561",
"panel.border": "#16161D",
"sideBar.border": "#16161D",
"panelSectionHeader.background": "#1f1f28",
"peekView.border": "#54546D",
"peekViewEditor.background": "#2A2A37",
"peekViewEditor.matchHighlightBackground": "#2D4F67",
"peekViewResult.background": "#363646",
"scrollbar.shadow": "#363646",
"scrollbarSlider.activeBackground": "#6f6f9080",
"scrollbarSlider.background": "#54546D66",
"scrollbarSlider.hoverBackground": "#54546D80",
"settings.focusedRowBackground": "#363646",
"settings.headerForeground": "#DCD7BA",
"sideBar.background": "#1F1F28",
"sideBar.foreground": "#DCD7BA",
"sideBarSectionHeader.background": "#363646",
"sideBarSectionHeader.foreground": "#DCD7BA",
"statusBar.background": "#16161D",
"statusBar.debuggingBackground": "#E82424",
"statusBar.debuggingBorder": "#957FB8",
"statusBar.debuggingForeground": "#DCD7BA",
"statusBar.foreground": "#C8C093",
"statusBar.noFolderBackground": "#1f1f28",
"statusBarItem.hoverBackground": "#363646",
"statusBarItem.remoteBackground": "#2D4F67",
"statusBarItem.remoteForeground": "#DCD7BA",
"tab.activeBackground": "#363646",
"tab.activeForeground": "#DCD7BA",
"tab.border": "#16161d",
"tab.hoverBackground": "#54546D",
"tab.inactiveBackground": "#1f1f28",
"tab.unfocusedHoverBackground": "#2A2A37",
"terminal.ansiBlack": "#1F1F28",
"terminal.ansiBlue": "#658594",
"terminal.ansiBrightBlack": "#2A2A37",
"terminal.ansiBrightBlue": "#7FB4CA",
"terminal.ansiBrightCyan": "#A3D4D5",
"terminal.ansiBrightGreen": "#98BB6C",
"terminal.ansiBrightMagenta": "#D27E99",
"terminal.ansiBrightRed": "#FF5D62",
"terminal.ansiBrightWhite": "#DCD7BA",
"terminal.ansiBrightYellow": "#E6C384",
"terminal.ansiCyan": "#9CABCA",
"terminal.ansiGreen": "#76946A",
"terminal.ansiMagenta": "#957FB8",
"terminal.ansiRed": "#E82424",
"terminal.ansiWhite": "#DCD7BA",
"terminal.ansiYellow": "#FF9E3B",
"terminal.background": "#1F1F28",
"terminal.border": "#16161D",
"terminal.foreground": "#DCD7BA",
"terminal.selectionBackground": "#223249",
"textBlockQuote.background": "#1F1F28",
"textBlockQuote.border": "#16161D",
"textLink.foreground": "#6A9589",
"textPreformat.foreground": "#FF9E3B",
"titleBar.activeBackground": "#363646",
"titleBar.activeForeground": "#DCD7BA",
"titleBar.inactiveBackground": "#1F1F28",
"titleBar.inactiveForeground": "#DCD7BA",
"walkThrough.embeddedEditorBackground": "#1F1F28"
},
"tokenColors": [
{
"name": "Comment",
"scope": [
"comment",
"punctuation.definition.comment"
],
"settings": {
"foreground": "#727169"
}
},
{
"name": "Variables",
"scope": [
"variable",
"string constant.other.placeholder"
],
"settings": {
"foreground": "#DCD7BA"
}
},
{
"name": "Colors",
"scope": [
"constant.other.color"
],
"settings": {
"foreground": "#FFA066"
}
},
{
"name": "Invalid",
"scope": [
"invalid",
"invalid.illegal"
],
"settings": {
"foreground": "#E82424"
}
},
{
"name": "Storage - Type",
"scope": [
"storage.type"
],
"settings": {
"foreground": "#957FB8"
}
},
{
"name": "Storage - Modifier",
"scope": [
"storage.modifier"
],
"settings": {
"foreground": "#957FB8"
}
},
{
"name": "Control Keyword",
"scope": [
"keyword.control.flow",
"keyword.control.conditional",
"keyword.control.loop"
],
"settings": {
"foreground": "#957FB8",
"fontStyle": "bold"
}
},
{
"name": "Operator, Misc",
"scope": [
"keyword.control",
"constant.other.color",
"meta.tag",
"keyword.other.template",
"keyword.other.substitution",
"keyword.other"
],
"settings": {
"foreground": "#957FB8"
}
},
{
"name": "ini file definition",
"scope": [
"keyword.other.definition.ini"
],
"settings": {
"foreground": "#FFA066"
}
},
{
"name": "Operator, Misc",
"scope": [
"keyword.control.trycatch"
],
"settings": {
"foreground": "#FF5D62",
"fontStyle": "bold"
}
},
{
"name": "Identifiers",
"scope": [
"keyword.other.unit",
"keyword.operator"
],
"settings": {
"foreground": "#C0A36E"
}
},
{
"name": "Punctuation, Brace",
"scope": [
"punctuation",
"punctuation.definition.tag",
"punctuation.separator.inheritance.php",
"punctuation.definition.tag.html",
"punctuation.definition.tag.begin.html",
"punctuation.definition.tag.end.html",
"punctuation.section.embedded",
"meta.brace",
"keyword.operator.type.annotation",
"keyword.operator.namespace"
],
"settings": {
"foreground": "#9CABCA"
}
},
{
"name": "Tag",
"scope": [
"entity.name.tag",
"meta.tag.sgml"
],
"settings": {
"foreground": "#E6C384"
}
},
{
"name": "HTML Tag",
"scope": [
"entity.name.tag.html"
],
"settings": {
"foreground": "#957FB8"
}
},
{
"name": "Function, Special Method",
"scope": [
"entity.name.function",
"meta.function-call",
"variable.function",
"support.function",
"keyword.other.special-method"
],
"settings": {
"foreground": "#7E9CD8"
}
},
{
"name": "Macto",
"scope": [
"entity.name.function.macro"
],
"settings": {
"foreground": "#FFA066"
}
},
{
"name": "Block Level Variables",
"scope": [
"meta.block variable.other"
],
"settings": {
"foreground": "#DCD7BA"
}
},
{
"name": "Other Variable",
"scope": [
"support.other.variable"
],
"settings": {
"foreground": "#DCD7BA"
}
},
{
"name": "String Link",
"scope": [
"string.other.link"
],
"settings": {
"foreground": "#FFA066"
}
},
{
"name": "Constant, Function Argument, Tag Attribute, Embedded",
"scope": [
"constant.numeric",
"constant.language",
"support.constant",
"constant.character",
"constant.escape"
],
"settings": {
"foreground": "#7FB4CA"
}
},
{
"name": "Boolean",
"scope": [
"constant.language.boolean"
],
"settings": {
"foreground": "#FFA066"
}
},
{
"name": "Number",
"scope": [
"constant.numeric"
],
"settings": {
"foreground": "#D27E99"
}
},
{
"name": "String, Symbols, Inherited Class, Markup Heading",
"scope": [
"string",
"punctuation.definition.string",
"constant.other.symbol",
"constant.other.key",
"entity.other.inherited-class",
"markup.heading",
"markup.inserted.git_gutter",
"meta.group.braces.curly constant.other.object.key.js string.unquoted.label.js",
"markup.inline.raw.string"
],
"settings": {
"foreground": "#98BB6C"
}
},
{
"name": "Class, Support",
"scope": [
"entity.name",
"support.type",
"support.class",
"support.other.namespace.use.php",
"meta.use.php",
"support.other.namespace.php",
"support.type.sys-types"
],
"settings": {
"foreground": "#7AA89F"
}
},
{
"name": "Namespace, Module",
"scope": [
"entity.name.type.module",
"entity.name.namespace"
],
"settings": {
"foreground": "#DCD7BA"
}
},
{
"name": "Go import",
"scope": [
"entity.name.import.go"
],
"settings": {
"foreground": "#98BB6C"
}
},
{
"name": "Property",
"scope": [
"variable.other.property"
],
"settings": {
"foreground": "#E6C384"
}
},
{
"name": "Import",
"scope": [
"keyword.control.import",
"keyword.import",
"meta.import"
],
"settings": {
"foreground": "#FFA066"
}
},
{
"name": "CSS Class and Support",
"scope": [
"source.css support.type.property-name",
"source.sass support.type.property-name",
"source.scss support.type.property-name",
"source.less support.type.property-name",
"source.stylus support.type.property-name",
"source.postcss support.type.property-name"
],
"settings": {
"foreground": "#7AA89F"
}
},
{
"name": "Sub-methods",
"scope": [
"entity.name.module.js",
"variable.import.parameter.js",
"variable.other.class.js"
],
"settings": {
"foreground": "#FF5D62"
}
},
{
"name": "Language methods",
"scope": [
"variable.language"
],
"settings": {
"foreground": "#FF5D62"
}
},
{
"name": "entity.name.method.js",
"scope": [
"entity.name.method.js"
],
"settings": {
"foreground": "#7E9CD8"
}
},
{
"name": "meta.method.js",
"scope": [
"meta.class-method.js entity.name.function.js",
"variable.function.constructor"
],
"settings": {
"foreground": "#7E9CD8"
}
},
{
"name": "Attributes",
"scope": [
"entity.other.attribute-name"
],
"settings": {
"foreground": "#957FB8"
}
},
{
"name": "HTML Attributes",
"scope": [
"entity.other.attribute-name.html",
"entity.other.attribute-name",
"invalid.deprecated.entity.other.attribute-name.html"
],
"settings": {
"foreground": "#E6C384"
}
},
{
"name": "CSS Classes",
"scope": [
"entity.other.attribute-name.class"
],
"settings": {
"foreground": "#E6C384"
}
},
{
"name": "CSS ID's",
"scope": [
"source.sass keyword.control"
],
"settings": {
"foreground": "#7FB4CA"
}
},
{
"name": "Inserted",
"scope": [
"markup.inserted"
],
"settings": {
"foreground": "#76946A"
}
},
{
"name": "Deleted",
"scope": [
"markup.deleted"
],
"settings": {
"foreground": "#C34043"
}
},
{
"name": "Changed",
"scope": [
"markup.changed"
],
"settings": {
"foreground": "#DCA561"
}
},
{
"name": "Regular Expressions",
"scope": [
"string.regexp"
],
"settings": {
"foreground": "#7FB4CA"
}
},
{
"name": "Escape Characters",
"scope": [
"constant.character.escape"
],
"settings": {
"foreground": "#7FB4CA"
}
},
{
"name": "URL",
"scope": [
"*url*",
"*link*",
"*uri*"
],
"settings": {
"fontStyle": "underline"
}
},
{
"name": "Decorators",
"scope": [
"tag.decorator.js entity.name.tag.js",
"tag.decorator.js punctuation.definition.tag.js"
],
"settings": {
"foreground": "#957FB8"
}
},
{
"name": "ES7 Bind Operator",
"scope": [
"source.js constant.other.object.key.js string.unquoted.label.js"
],
"settings": {
"foreground": "#FF5D62"
}
},
{
"name": "JSON Key - Level 0",
"scope": [
"source.json meta.structure.dictionary.json support.type.property-name.json"
],
"settings": {
"foreground": "#D27E99"
}
},
{
"name": "JSON Key - Level 1",
"scope": [
"source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json support.type.property-name.json"
],
"settings": {
"foreground": "#E6C384"
}
},
{
"name": "JSON Key - Level 2",
"scope": [
"source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json support.type.property-name.json"
],
"settings": {
"foreground": "#FFA066"
}
},
{
"name": "JSON Key - Level 3",
"scope": [
"source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json support.type.property-name.json"
],
"settings": {
"foreground": "#FF5D62"
}
},
{
"name": "JSON Key - Level 4",
"scope": [
"source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json support.type.property-name.json"
],
"settings": {
"foreground": "#FFA066"
}
},
{
"name": "JSON Key - Level 5",
"scope": [
"source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json support.type.property-name.json"
],
"settings": {
"foreground": "#7E9CD8"
}
},
{
"name": "JSON Key - Level 6",
"scope": [
"source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json support.type.property-name.json"
],
"settings": {
"foreground": "#D27E99"
}
},
{
"name": "JSON Key - Level 7",
"scope": [
"source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json support.type.property-name.json"
],
"settings": {
"foreground": "#957FB8"
}
},
{
"name": "JSON Key - Level 8",
"scope": [
"source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json support.type.property-name.json"
],
"settings": {
"foreground": "#98BB6C"
}
},
{
"name": "Markdown - Plain",
"scope": [
"text.html.markdown",
"punctuation.definition.list_item.markdown"
],
"settings": {
"foreground": "#DCD7BA"
}
},
{
"name": "Markdown - Markup Raw Inline",
"scope": [
"text.html.markdown markup.inline.raw.markdown"
],
"settings": {
"foreground": "#957FB8"
}
},
{
"name": "Markdown - Markup Raw Inline Punctuation",
"scope": [
"text.html.markdown markup.inline.raw.markdown punctuation.definition.raw.markdown"
],
"settings": {
"foreground": "#957FB8"
}
},
{
"name": "Markdown - Heading",
"scope": [
"markdown.heading",
"entity.name.section.markdown",
"markup.heading.markdown"
],
"settings": {
"foreground": "#7E9CD8"
}
},
{
"name": "Markup - Italic",
"scope": [
"markup.italic"
],
"settings": {
"fontStyle": "italic",
"foreground": "#E46876"
}
},
{
"name": "Markup - Bold",
"scope": [
"markup.bold",
"markup.bold string"
],
"settings": {
"fontStyle": "bold"
}
},
{
"name": "Markup - Bold-Italic",
"scope": [
"markup.bold markup.italic",
"markup.italic markup.bold",
"markup.quote markup.bold",
"markup.bold markup.italic string",
"markup.italic markup.bold string",
"markup.quote markup.bold string"
],
"settings": {
"fontStyle": "bold",
"foreground": "#E46876"
}
},
{
"name": "Markup - Underline",
"scope": [
"markup.underline"
],
"settings": {
"fontStyle": "underline",
"foreground": "#7FB4CA"
}
},
{
"name": "Markdown - Blockquote",
"scope": [
"markup.quote punctuation.definition.blockquote.markdown"
],
"settings": {
"foreground": "#727169"
}
},
{
"name": "Markup - Quote",
"scope": [
"markup.quote"
],
"settings": {
"fontStyle": "italic"
}
},
{
"name": "Markdown - Link",
"scope": [
"string.other.link.title.markdown"
],
"settings": {
"foreground": "#FFA066"
}
},
{
"name": "Markdown - Link Description",
"scope": [
"string.other.link.description.title.markdown"
],
"settings": {
"foreground": "#957FB8"
}
},
{
"name": "Markdown - Link Anchor",
"scope": [
"constant.other.reference.link.markdown"
],
"settings": {
"foreground": "#E6C384"
}
},
{
"name": "Markup - Raw Block",
"scope": [
"markup.raw.block"
],
"settings": {
"foreground": "#957FB8"
}
},
{
"name": "Markdown - Raw Block Fenced",
"scope": [
"markup.raw.block.fenced.markdown"
],
"settings": {
"foreground": "#727169"
}
},
{
"name": "Markdown - Fenced Bode Block",
"scope": [
"punctuation.definition.fenced.markdown"
],
"settings": {
"foreground": "#727169"
}
},
{
"name": "Markdown - Fenced Bode Block Variable",
"scope": [
"markup.raw.block.fenced.markdown",
"variable.language.fenced.markdown",
"punctuation.section.class.end"
],
"settings": {
"foreground": "#DCD7BA"
}
},
{
"name": "Markdown - Fenced Language",
"scope": [
"variable.language.fenced.markdown"
],
"settings": {
"foreground": "#727169"
}
},
{
"name": "Markdown - Separator",
"scope": [
"meta.separator"
],
"settings": {
"fontStyle": "bold",
"foreground": "#9CABCA"
}
},
{
"name": "Markup - Table",
"scope": [
"markup.table"
],
"settings": {
"foreground": "#DCD7BA"
}
}
],
"semanticTokenColors": {
"parameter": "#DCD7BA",
"variable": "#DCD7BA",
"arithmetic": "#C0A36E",
"method": "#7E9CD8",
"function": "#7E9CD8",
"operator": "#C0A36E",
"parameter.declaration": "#E6C384",
"parameter.definition": "#E6C384",
"variable.readonly": "#FFA066",
"variable.readonly.local": "#DCD7BA",
"variable.readonly.defaultLibrary": "#7FB4CA",
"macro": "#FFA066",
"keyword.controlFlow": {
"foreground": "#957FB8",
"fontStyle": "bold"
}
}
}
+5
View File
@@ -0,0 +1,5 @@
import fs from 'fs';
export const kanagawa_dark = JSON.parse(
fs.readFileSync('./src/lib/kanagawa.json', 'utf-8')
)
+116 -50
View File
@@ -5,10 +5,11 @@
import Footer from '$lib/components/footer.svelte'; import Footer from '$lib/components/footer.svelte';
import * as publish from '$lib/publish'; import * as publish from '$lib/publish';
import FormattedDate from '$lib/components/formatted_date.svelte'; import FormattedDate from '$lib/components/formatted_date.svelte';
import Head from '$lib/components/head.svelte'; import Overview from '$lib/components/overview.svelte';
import type { Article } from '$lib/article'; import type { Article } from '$lib/article';
export let data: Article; export let data: Article;
const isUpdated = data.updated_at.getTime() != data.released_at.getTime();
</script> </script>
<svelte:head> <svelte:head>
@@ -18,10 +19,10 @@
<meta property="og:image" content={data.image} /> <meta property="og:image" content={data.image} />
<meta property="og:type" content="article" /> <meta property="og:type" content="article" />
<meta property="article:published_time" content={data.released_at.toISOString()} /> <meta property="article:published_time" content={data.released_at.toISOString()} />
{#if data.updated_at} {#if isUpdated}
<meta property="article:modified_time" content={data.updated_at.toISOString()} /> <meta property="article:modified_time" content={data.updated_at.toISOString()} />
{/if} {/if}
<meta property="article:author" content="HareWorks" /> <meta property="article:author" content={data.author} />
<meta property="article:section" content="Blog" /> <meta property="article:section" content="Blog" />
<meta property="article:tag" content={data.tags.join(',')} /> <meta property="article:tag" content={data.tags.join(',')} />
<meta name="robots" content={data.publish as publish.Publish} /> <meta name="robots" content={data.publish as publish.Publish} />
@@ -32,30 +33,46 @@
</div> --> </div> -->
<div class="container"> <div class="container">
<main> <main>
<div class="title"> <div class="content">
<h1>{data.title}</h1> <div class="title">
<div class="meta"> <h1>{data.title}</h1>
<span> <div class="meta">
released <FormattedDate date={data.released_at} /> <div class="left">
{#if data.updated_at}<br /> <span>
updated <FormattedDate date={data.updated_at} /> released <FormattedDate date={data.released_at} />
{/if} {#if isUpdated}<br />
</span> updated <FormattedDate date={data.updated_at} />
<span> {/if}
<a href="/category/{data.category}" </span>
>{data.category[0].toUpperCase() + data.category.slice(1)}</a <span>
> <a href="/category/{data.category}"
{#each data.tags as tag} >{data.category[0].toUpperCase() + data.category.slice(1)}</a
<a href={`/search?tag=${tag}`}>{tag}</a> >
{/each} {#each data.tags as tag}
</span> <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>
<div class="panel">
<div class="document">
<slot />
</div>
<Footer />
</div> </div>
</div> </div>
<div class="panel"> <div class="card-container">
<div class="document"> <div class="card">
<slot /> <Overview html={data.content} />
</div> </div>
<Footer />
</div> </div>
</main> </main>
</div> </div>
@@ -91,26 +108,48 @@
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
} }
main { main {
border: 1px solid var(--line-primary);
border-top: none;
border-bottom: none;
box-sizing: border-box; box-sizing: border-box;
color: white; 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: 100px 100px 0 100px;
max-width: 900px;
margin: 0;
}
}
.card-container {
position: sticky;
top: 0;
background-color: var(--background-primary);
box-sizing: border-box;
padding: 20px; padding: 20px;
padding-top: 100px;
width: 1000px;
margin: 0 auto;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between;
overflow: visible;
.card {
width: 100px;
padding: 0.5em 0;
overflow: visible;
}
} }
.title { .title {
h1 { h1 {
font-size: 2rem; font-size: 2rem;
text-align: center; text-align: center;
margin: 0; margin: 0 -30px 0 -30px;
padding: 12px 100px 12px 0; padding: 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.3); border-bottom: 1px solid rgba(255, 255, 255, 0.3);
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
@@ -120,29 +159,56 @@
.meta { .meta {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
flex-direction: row;
padding: 0 20px; padding: 0 20px;
font-size: 0.8rem; font-size: 1rem;
span { > .left {
opacity: 0.6; display: flex;
} flex-direction: column;
a { justify-content: center;
margin-left: 5px; align-items: left;
padding: 0 5px; span {
color: inherit; opacity: 0.6;
text-decoration: none;
&:hover {
text-decoration: underline;
} }
&:first-child { a {
&::before { margin-left: 5px;
content: '🗀 '; padding: 0 5px;
color: inherit;
text-decoration: none;
&:hover {
text-decoration: underline;
} }
&::after { &:first-child {
content: '|'; margin-left: 0;
margin-left: 15px; padding-left: 0;
&::before {
content: '🗀 ';
}
&::after {
content: '|';
margin-left: 15px;
}
} }
} }
} }
> .right {
display: flex;
flex-direction: row;
justify-content: right;
align-items: center;
gap: 10px;
height: 50px;
margin: 10px;
img {
width: 50px;
height: 50px;
border-radius: 15%;
}
div {
font-size: 1rem;
}
}
} }
} }
.panel { .panel {
+1
View File
@@ -0,0 +1 @@
html{-webkit-text-size-adjust:100%;box-sizing:border-box;-moz-tab-size:4;tab-size:4;word-break:normal}*,:after,:before{background-repeat:no-repeat;box-sizing:inherit}:after,:before{text-decoration:inherit;vertical-align:inherit}*{margin:0;padding:0}hr{color:inherit;height:0;overflow:visible}details,main{display:block}summary{display:list-item}small{font-size:80%}[hidden]{display:none}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}a{background-color:transparent}a:active,a:hover{outline-width:0}code,kbd,pre,samp{font-family:monospace,monospace}pre{font-size:1em}b,strong{font-weight:bolder}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-color:inherit;text-indent:0}iframe{border-style:none}input{border-radius:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}textarea{overflow:auto;resize:vertical}button,input,optgroup,select,textarea{font:inherit}optgroup{font-weight:700}button{overflow:visible}button,select{text-transform:none}[role=button],[type=button],[type=reset],[type=submit],button{cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button:-moz-focusring{outline:1px dotted ButtonText}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}button,input,select,textarea{background-color:transparent;border-style:none}a:focus,button:focus,input:focus,select:focus,textarea:focus{outline-width:0}select{-moz-appearance:none;-webkit-appearance:none}select::-ms-expand{display:none}select::-ms-value{color:currentColor}legend{border:0;color:inherit;display:table;max-width:100%;white-space:normal}::-webkit-file-upload-button{-webkit-appearance:button;color:inherit;font:inherit}[disabled]{cursor:default}img{border-style:none}progress{vertical-align:baseline}[aria-busy=true]{cursor:progress}[aria-controls]{cursor:pointer}[aria-disabled=true]{cursor:default}
+20
View File
@@ -0,0 +1,20 @@
export const format = (
date: Date
) => {
const pad = function (str: string): string {
return ('0' + str).slice(-2);
};
const year = date.getFullYear().toString();
const month = pad((date.getMonth() + 1).toString());
const day = pad(date.getDate().toString());
const hour = pad(date.getHours().toString());
const min = pad(date.getMinutes().toString());
const sec = pad(date.getSeconds().toString());
const tz = -date.getTimezoneOffset();
const sign = tz >= 0 ? '+' : '-';
const tzHour = pad((tz / 60).toString());
const tzMin = pad((tz % 60).toString());
return `${year}-${month}-${day}T${hour}:${min}:${sec}${sign}${tzHour}:${tzMin}`;
};
+25
View File
@@ -0,0 +1,25 @@
//@ts-ignore
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],
langs: ["javascript", "typescript", "shell"],
});
export default async function compileArticle(code: string) {
return compile(code, {
highlight: {
highlighter: async (code: string, lang = "text") => {
const html = escapeSvelte(
highlighter.codeToHtml(code, { lang, theme }),
);
return html;
},
},
rehypePlugins: [rehypeAutolinkHeadings, rehypeSlug],
});
}
+144
View File
@@ -0,0 +1,144 @@
import type { Postgres } from '$lib/server/database/postgres';
import { type SimpleGit, CheckRepoActions } from 'simple-git';
import fs from 'fs';
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';
const load = async (db: Postgres, git: SimpleGit) => {
const articleFiles = await crawlArticles(db);
await db.query('update tag set ref_count = 0');
await db.begin();
let i = 0;
for (const { path, id } of articleFiles) {
await db.savepoint(`load${i}`);
console.log(`Processing ${id}...`);
try {
const gitlog = await git.log({
file: path.slice(11),
strictDate: true,
});
const res = await db.query('select * from article where id = $1', [id]);
// console.log(gitlog);
const latest = gitlog.latest!;
const oldest = gitlog.all[gitlog.all.length - 1];
const author = oldest.author_name;
const email = oldest.author_email;
const released_at = new Date(oldest.date);
const updated_at = new Date(latest.date);
console.log(`Author: ${author} <${email}>\nReleased at: ${format(released_at)}\nUpdated at: ${format(updated_at)}`);
const category = path.split('/')[3];
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;
const publish = compiled.data.fm.publish;
const content = compiled.code
.replace(/>{@html `<code class="language-/g, '><code class="language-')
.replace(/<\/code>`}<\/pre>/g, '</code></pre>')
if (res.rowCount == 0) {
console.log(`New article: ${id}`);
await db.query(
'insert into article (id, released_at, updated_at, author, email, title, category, tags, image, publish, content) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)',
[id, released_at, updated_at, author, email, title, category, tags, image, publish, content]
);
// } else if (res.rows[0].updated_at < updated_at) {
} else if (true) {
console.log(`Update article: ${id}`);
await db.query(
'update article set released_at = $2, updated_at = $3, author = $4, email = $5, title = $6, category = $7, tags = $8, image = $9, publish = $10, content = $11 where id = $1',
[id, released_at, updated_at, author, email, title, category, tags, image, publish, content]
);
} else {
console.log(`Article ${id} is already up-to-date`);
}
for (const tag of tags) {
if ((await db.query('select * from tag where name = $1', [tag])).rowCount == 0) {
db.query('insert into tag (name, ref_count) values ($1, 1)', [tag]);
} else {
db.query('update tag set ref_count = ref_count + 1 where name = $1', [tag]);
}
}
} catch (err) {
console.log(err);
await db.rollbackTo(`load${i}`);
} finally {
console.log('');
await db.releaseSavepoint(`load${i}`);
i++;
}
}
await db.commit();
}
type ArticleFileItem = {
path: string,
id: string,
}
const cloneRepo = async (git: SimpleGit) => {
const isRepoRoot = await git.checkIsRepo(CheckRepoActions.IS_REPO_ROOT);
if (isRepoRoot) {
console.log('Pulling articles from git..');
await git.pull();
} else {
console.log('Cloning articles from git..');
await git.clone('git@gitea.hareworks.net:Hare/blog-articles.git', './');
}
}
const createTable = async (db: Postgres, schemas: TableSchema[]) => {
await db.begin();
try {
for (const schema of schemas) {
const res = await db.query(`select * from information_schema.tables where table_name = '${schema.name}'`)
if (res.rowCount == 0) {
console.log(`Creating table ${schema.name}`);
const columnStr = schema.columns.map(c => `${c.name} ${c.type} ${c.constraint}`).join(', ');
await db.query(`create table ${schema.name} (${columnStr})`);
} else {
console.log(`Table ${schema.name} already exists`);
}
}
} catch (err) {
console.error(err);
await db.rollback();
} finally {
await db.commit();
}
}
const crawlArticles = async (db: Postgres): Promise<ArticleFileItem[]> => {
const articleFiles: ArticleFileItem[] = [];
function scanDir(path: string) {
const files = fs.readdirSync(path);
for (const file of files) {
const dir = `${path}/${file}`;
const stat = fs.statSync(dir);
if (stat.isDirectory()) {
scanDir(dir);
} else {
articleFiles.push({ path: `${path}/${file}`, id: file.replace('.md', '') });
}
}
}
scanDir('./articles/article');
return articleFiles;
}
export async function init(db: Postgres, git: SimpleGit) {
await createTable(db, server_table);
await cloneRepo(git);
await load(db, git);
}
export async function reload(db: Postgres, git: SimpleGit) {
await cloneRepo(git);
await load(db, git);
}
-15
View File
@@ -1,15 +0,0 @@
import pg from 'pg';
const { Pool } = pg;
import {
PG_USER,
PG_PASS,
PG_HOST,
PG_PORT,
PG_DB,
} from '$env/static/private'
const connectionString = `postgres://${PG_USER}:${PG_PASS}@${PG_HOST}:${PG_PORT}/${PG_DB}`;
const pool = new Pool({ connectionString });
export const getConnection = () => pool;
-165
View File
@@ -1,165 +0,0 @@
// initialize
import type { Postgres } from '$lib/server/database';
import fs from 'fs';
import { compile } from 'mdsvex';
export default async function init(db: Postgres) {
// Create tables(when not exists)
const schemas = [
{
name: 'article',
columns: [
{ name: 'seq', type: 'serial', constraint: 'primary key' },
{ name: 'id', type: 'text', constraint: 'not null' },
{ name: 'title', type: 'text', constraint: 'not null' },
{ name: 'category', type: 'text', constraint: 'not null' },
{ name: 'released_at', type: 'timestamp', constraint: 'not null' },
{ name: 'updated_at', type: 'timestamp', constraint: 'not null' },
{ name: 'tags', type: 'text[]', constraint: 'not null' },
{ name: 'image', type: 'text', constraint: '' },
{ name: 'publish', type: 'text', constraint: 'not null' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'article_comment',
columns: [
{ name: 'id', type: 'serial', constraint: 'primary key' },
{ name: 'article', type: 'integer', constraint: 'not null' },
{ name: 'posted_at', type: 'timestamp', constraint: 'not null' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'thread',
columns: [
{ name: 'seq', type: 'serial', constraint: 'primary key' },
{ name: 'id', type: 'text', constraint: 'not null' },
{ name: 'title', type: 'text', constraint: 'not null' },
{ name: 'category', type: 'text', constraint: 'not null' },
{ name: 'created_at', type: 'timestamp', constraint: 'not null' },
{ name: 'updated_at', type: 'timestamp', constraint: 'not null' },
{ name: 'tags', type: 'text[]', constraint: 'not null' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'thread_post',
columns: [
{ name: 'seq', type: 'serial', constraint: 'primary key' },
{ name: 'thread_id', type: 'integer', constraint: 'not null' },
{ name: 'title', type: 'text', constraint: 'not null' },
{ name: 'posted_at', type: 'timestamp', constraint: 'not null' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'thread_comment',
columns: [
{ name: 'id', type: 'serial', constraint: 'primary key' },
{ name: 'thread', type: 'integer', constraint: 'not null' },
{ name: 'posted_at', type: 'timestamp', constraint: 'not null' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'tag',
columns: [
{ name: 'seq', type: 'serial', constraint: 'primary key' },
{ name: 'name', type: 'text', constraint: 'not null' },
{ name: 'ref_count', type: 'integer', constraint: 'not null' },
],
}
];
await db.begin();
try {
// Create tables
for (const schema of schemas) {
const res = await db.query(`select * from information_schema.tables where table_name = '${schema.name}'`)
if (res.rowCount == 0) {
console.log(`Creating table ${schema.name}`);
const columnStr = schema.columns.map(c => `${c.name} ${c.type} ${c.constraint}`).join(', ');
await db.query(`create table ${schema.name} (${columnStr})`);
} else {
console.log(`Table ${schema.name} already exists`);
}
}
await db.commit();
const articleFiles: ArticleFileItem[] = [];
function scanDir(path: string) {
const files = fs.readdirSync(path);
for (const file of files) {
const dir = `${path}/${file}`;
const stat = fs.statSync(dir);
if (stat.isDirectory()) {
scanDir(dir);
} else {
articleFiles.push({ path: `${path}/${file}`, id: file });
}
}
}
scanDir('./articles/article');
await db.query('update tag set ref_count = 0');
db.commit();
for (const { path, id } of articleFiles) {
const res = await db.query('select * from article where id = $1', [id]);
const compiled = await compile(fs.readFileSync(path, 'utf-8'));
const title = compiled.data.fm.title;
const category = path.split('/')[3];
const tags: string[] = compiled.data.fm.tags;
const released_at = new Date(compiled.data.fm.released_at);
const updated_at = new Date(compiled.data.fm.updated_at);
const image = compiled.data.fm.image;
const publish = compiled.data.fm.publish;
const content = compiled.code
.replace(/>{@html `<code class="language-/g, '><code class="language-')
.replace(/<\/code>`}<\/pre>/g, '</code></pre>');
if (res.rowCount == 0) {
console.log(`New article: ${id}`);
await db.query(
'insert into article (id, title, category, released_at, updated_at, tags, image, publish, content) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)',
[id, title, category, released_at, updated_at, tags, image, publish, content]
);
} else if (res.rows[0].updated_at < updated_at) {
console.log(`Update article: ${id}`);
await db.query(
'update article set title = $2, category = $3, released_at = $4, updated_at = $5, tags = $6, image = $7, publish = $8, content = $9 where id = $1',
[id, title, category, released_at, updated_at, tags, image, publish, content]
);
} else {
console.log(`Article ${id} is already up-to-date`);
}
for (const tag of tags) {
if ((await db.query('select * from tag where name = $1', [tag])).rowCount == 0) {
db.query('insert into tag (name, ref_count) values ($1, 1)', [tag]);
} else {
db.query('update tag set ref_count = ref_count + 1 where name = $1', [tag]);
}
}
}
await db.commit();
} catch (err) {
console.error(err);
await db.rollback();
} finally {
await db.release();
}
}
type ArticleFileItem = {
path: string,
id: string,
}
export type TableSchema = {
name: string,
columns: {
name: string,
type: string,
constraint: string,
}[],
}
+24
View File
@@ -0,0 +1,24 @@
import pg from 'pg';
const { Pool } = pg;
import {
PG_USER,
PG_PASS,
PG_HOST,
PG_PORT,
PG_DB,
} from '$env/static/private'
const connectionString = `postgres://${PG_USER}:${PG_PASS}@${PG_HOST}:${PG_PORT}/${PG_DB}`;
let pool = new Pool({ connectionString });
export const getPool = pool;
export const reConnect = async () => {
pool = new Pool({ connectionString });
}
import { init as init_data} from '$lib/server/data_loader';
import PG from '$lib/server/database/postgres';
import git from '$lib/server/git';
await init_data(await PG(pool), git);
@@ -1,12 +1,10 @@
import pg from 'pg'; import pg from 'pg';
const { Pool } = pg;
import { getConnection } from './database/get_connection';
export class Postgres { export class Postgres {
client: pg.PoolClient | null = null; client: pg.PoolClient | null = null;
public static async new(pool: Pool) { public static async new(pool: pg.Pool) {
const pg = new Postgres(); const pg = new Postgres();
pg.client = await getConnection().connect(); pg.client = await pool.connect();
return pg; return pg;
} }
@@ -26,15 +24,23 @@ export class Postgres {
await this.client!.query('commit'); await this.client!.query('commit');
} }
async savepoint(name: string) {
await this.client!.query(`savepoint ${name}`);
}
async rollback() { async rollback() {
await this.client!.query('rollback'); await this.client!.query('rollback');
} }
async rollbackTo(name: string) {
await this.client!.query(`rollback to ${name}`);
}
async releaseSavepoint(name: string) {
await this.client!.query(`release savepoint ${name}`);
}
} }
export default async ( export default async (
pool: Pool pool: pg.Pool
) => { return await Postgres.new(pool); }; ) => { return await Postgres.new(pool); };
import { building } from '$app/environment';
import init from '$lib/server/database/init_db';
if (!building) await init(await Postgres.new(getConnection()));
+77
View File
@@ -0,0 +1,77 @@
export type TableSchema = {
name: string,
columns: {
name: string,
type: string,
constraint: string,
}[],
}
export default [
{
name: 'article',
columns: [
{ name: 'seq', type: 'serial', constraint: 'primary key' },
{ name: 'id', type: 'text', constraint: 'not null' },
{ name: 'released_at', type: 'timestamp', constraint: 'not null' },
{ name: 'updated_at', type: 'timestamp', constraint: 'not null' },
{ name: 'author', type: 'text', constraint: 'not null' },
{ name: 'email', type: 'text', constraint: 'not null' },
{ name: 'title', type: 'text', constraint: 'not null' },
{ name: 'category', type: 'text', constraint: 'not null' },
{ name: 'tags', type: 'text[]', constraint: 'not null' },
{ name: 'image', type: 'text', constraint: '' },
{ name: 'publish', type: 'text', constraint: 'not null' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'article_comment',
columns: [
{ name: 'id', type: 'serial', constraint: 'primary key' },
{ name: 'article', type: 'integer', constraint: 'not null' },
{ name: 'posted_at', type: 'timestamp', constraint: 'not null' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'thread',
columns: [
{ name: 'seq', type: 'serial', constraint: 'primary key' },
{ name: 'id', type: 'text', constraint: 'not null' },
{ name: 'title', type: 'text', constraint: 'not null' },
{ name: 'category', type: 'text', constraint: 'not null' },
{ name: 'created_at', type: 'timestamp', constraint: 'not null' },
{ name: 'updated_at', type: 'timestamp', constraint: 'not null' },
{ name: 'tags', type: 'text[]', constraint: 'not null' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'thread_post',
columns: [
{ name: 'seq', type: 'serial', constraint: 'primary key' },
{ name: 'thread_id', type: 'integer', constraint: 'not null' },
{ name: 'title', type: 'text', constraint: 'not null' },
{ name: 'posted_at', type: 'timestamp', constraint: 'not null' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'thread_comment',
columns: [
{ name: 'id', type: 'serial', constraint: 'primary key' },
{ name: 'thread', type: 'integer', constraint: 'not null' },
{ name: 'posted_at', type: 'timestamp', constraint: 'not null' },
{ name: 'content', type: 'text', constraint: 'not null' },
],
},
{
name: 'tag',
columns: [
{ name: 'seq', type: 'serial', constraint: 'primary key' },
{ name: 'name', type: 'text', constraint: 'not null' },
{ name: 'ref_count', type: 'integer', constraint: 'not null' },
],
}
] as TableSchema[];
+17
View File
@@ -0,0 +1,17 @@
import fs from 'fs';
import { simpleGit, } from 'simple-git';
import type { SimpleGit, SimpleGitOptions } from 'simple-git';
console.log(process.cwd()+'/articles');
fs.mkdirSync(process.cwd()+'/articles', { recursive: true });
export const gitOptions: Partial<SimpleGitOptions> = {
baseDir: process.cwd()+'/articles',
binary: 'git',
maxConcurrentProcesses: 6,
config: [
'core.sshCommand=ssh -i ../key -F /dev/null'
],
};
const git: SimpleGit = simpleGit(gitOptions);
export default git;
+15 -2
View File
@@ -1,5 +1,18 @@
<script> <script>
import '$lib/app.scss'; import '$lib/app.scss';
</script> </script>
<slot /> <svelte:head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-6D99J1R7DX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-6D99J1R7DX');
</script>
</svelte:head>
<slot />
+3 -2
View File
@@ -1,6 +1,7 @@
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
import PG from '$lib/server/database'; import PG from '$lib/server/database/postgres';
import { format } from '$lib/scripts/formatted_date';
let data: { let data: {
recent: { recent: {
@@ -28,7 +29,7 @@ export const load: PageServerLoad = async ({ params, locals }) => {
data.recent = recent_articles.rows.map((row) => ({ data.recent = recent_articles.rows.map((row) => ({
title: row.title, title: row.title,
image: row.image, image: row.image,
date: row.updated_at.toISOString().slice(0, 10), date: format(row.updated_at),
link: `/article/${row.category}/${row.id}`, link: `/article/${row.category}/${row.id}`,
tags: row.tags, tags: row.tags,
})); }));
+6 -6
View File
@@ -130,7 +130,7 @@
height: 100dvh; height: 100dvh;
backdrop-filter: blur(2px); backdrop-filter: blur(2px);
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
} }
.controls { .controls {
position: fixed; position: fixed;
@@ -305,15 +305,15 @@
.tags { .tags {
ul { ul {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: left; justify-content: left;
flex-direction: row; flex-direction: row;
gap: 3px; gap: 3px;
li { li {
font-size: 1rem; font-size: 1rem;
padding: 3px 5px; padding: 3px 5px;
border: 1px solid var(--line-primary); border: 1px solid var(--line-primary);
border-radius: 5px; border-radius: 5px;
} }
} }
} }
+22
View File
@@ -0,0 +1,22 @@
import { json } from '@sveltejs/kit';
import { error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import {
TOKEN
} from '$env/static/private'
import PG from '$lib/server/database/postgres';
import { building } from '$app/environment';
import { reload as reload_data } from '$lib/server/data_loader';
export const POST: RequestHandler = async ({ url, locals }) => {
const token = url.searchParams.get('token');
console.log(token);
if (token !== TOKEN) {
return error(401, 'Unauthorized');
}
if (!building) await reload_data(await PG(locals.db), locals.git);
return new Response(String(token));
};
+18
View File
@@ -0,0 +1,18 @@
import { json } from '@sveltejs/kit';
import { error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async ({ url, locals }) => {
const category = String(url.searchParams.get('category'));
if (!category) {
error(400, 'Bad request');
}
const db = await locals.db;
const article = await db.query(
'SELECT * FROM article WHERE category = $1',
[category]
);
return new Response();
};
@@ -1,6 +1,6 @@
import type { Article } from '$lib/article'; import type { Article } from '$lib/article';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
import PG from '$lib/server/database'; import PG from '$lib/server/database/postgres';
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
export const load: PageServerLoad = async ({ params, locals }) => { export const load: PageServerLoad = async ({ params, locals }) => {
@@ -22,7 +22,7 @@ export const load: PageServerLoad = async ({ params, locals }) => {
return data return data
} catch (e) { } catch (e) {
await db.rollback(); await db.rollback();
error(500, (e as Error).message); error(404, 'Not found');
} finally { } finally {
await db.release(); await db.release();
} }
@@ -1,6 +1,6 @@
import type { Article } from '$lib/article'; import type { Article } from '$lib/article';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
import PG from '$lib/server/database'; import PG from '$lib/server/database/postgres';
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
export const load: PageServerLoad = async ({ params, locals }) => { export const load: PageServerLoad = async ({ params, locals }) => {
@@ -22,7 +22,7 @@ export const load: PageServerLoad = async ({ params, locals }) => {
return data return data
} catch (e) { } catch (e) {
await db.rollback(); await db.rollback();
error(500, (e as Error).message); error(404, 'Not found');
} finally { } finally {
await db.release(); await db.release();
} }
+1 -1
View File
@@ -1,5 +1,5 @@
import { redirect } from '@sveltejs/kit'; import { redirect } from '@sveltejs/kit';
export function load() { export function load() {
throw redirect(308, 'https://discord.gg/9RfkJeQQfB'); throw redirect(308, 'https://discord.gg/hbHzw7JkFq');
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 10 KiB

-5
View File
@@ -3,14 +3,9 @@ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */ /** @type {import('@sveltejs/kit').Config} */
const config = { const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(), preprocess: vitePreprocess(),
kit: { 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() adapter: adapter()
} }
}; };