Compare commits
192 commits
55648926cc
...
5612fc5cea
Author | SHA1 | Date | |
---|---|---|---|
|
5612fc5cea | ||
|
bb907ba1fc | ||
|
b15c3ae1cb | ||
|
6a46db88b4 | ||
|
7860de0a9f | ||
|
5e2805658b | ||
|
cdcba10cd7 | ||
|
00eaaa1095 | ||
|
4036684462 | ||
|
18487c1c4c | ||
|
74c08e6e3b | ||
|
f294662f4c | ||
|
c5026b4751 | ||
|
1cf978f7f8 | ||
|
c141924464 | ||
|
f136f65090 | ||
|
6e96aede25 | ||
|
fd6bb3b7aa | ||
|
1f04658852 | ||
|
e6dddea4ac | ||
|
424a4dd8f9 | ||
|
a03602e4c2 | ||
|
042610eaff | ||
|
87d3c8614f | ||
|
1ac63adbea | ||
|
264810b57c | ||
|
4caccfb44e | ||
|
869e284255 | ||
|
558c43f2ae | ||
|
bc54ba32bb | ||
|
58ecbd0cd6 | ||
|
fc61fa5d70 | ||
|
a4e55d514f | ||
|
3d1c5518d0 | ||
|
784006f2de | ||
|
f4aa175030 | ||
|
5c8d0009b5 | ||
|
de598d5d8d | ||
|
e656f74da5 | ||
|
3615e90403 | ||
|
72de5eda03 | ||
|
02bad37340 | ||
|
986d25d583 | ||
|
b18b150f68 | ||
|
d927c0ef09 | ||
|
62c47c701e | ||
|
34691d4b4c | ||
|
2cfc5b2783 | ||
|
5993aa2a6b | ||
|
a5998f5849 | ||
|
bc4c959a29 | ||
|
cec524f59b | ||
|
03ee044482 | ||
|
c9e8c38cfa | ||
|
5d1b25c678 | ||
|
3b70f808ec | ||
|
d995dd5cb6 | ||
|
254ccca9b1 | ||
|
002f1d55f7 | ||
|
17a039a043 | ||
|
09ad19e627 | ||
|
55ed0bc3e3 | ||
|
f9276a11a9 | ||
|
8c0baefb9c | ||
|
6768187eb2 | ||
|
cfe746dfd6 | ||
|
1a32b7e9b8 | ||
|
3c28b61096 | ||
|
9acbd6721b | ||
|
9519c931a4 | ||
|
a72a21c688 | ||
|
5e39cd0012 | ||
|
ed65720a39 | ||
|
1481384339 | ||
|
83a5509ef0 | ||
|
6583df822e | ||
|
ffd76f5d56 | ||
|
b604050d9f | ||
|
bae3b10a94 | ||
|
a8a6fd43d4 | ||
|
94a4949f21 | ||
|
af5e4e100a | ||
|
fcc51c609f | ||
|
5ea4b9a171 | ||
|
89671e6b1a | ||
|
48bd5b970f | ||
|
3301fed08c | ||
|
7ebaa57d25 | ||
|
c7a853aa8d | ||
|
27cbb38784 | ||
|
70b196ab55 | ||
|
32a20d18c6 | ||
|
2e39f0d002 | ||
|
761e1579c9 | ||
|
698cbf23ee | ||
|
129f94256b | ||
|
12d4308382 | ||
|
c7e4fa0743 | ||
|
de9a6dc1d6 | ||
125e2d8a3f | |||
934175040a | |||
fba7b6caa8 | |||
6150cb99b5 | |||
f873231ff5 | |||
56a6b35969 | |||
522903bf66 | |||
8a7ce1334f | |||
6df32981ed | |||
a60fffe045 | |||
06d2daa57e | |||
|
7a20781835 | ||
|
f37e97a659 | ||
|
a7f98f64ed | ||
|
d1e6708372 | ||
|
8207fc37c4 | ||
|
9f19f3def6 | ||
|
79d53a8305 | ||
|
7d2add5908 | ||
|
de5b2eb855 | ||
|
544d1329f1 | ||
|
9a5a32b84d | ||
|
85e59ea101 | ||
|
565f9a4741 | ||
|
ceeaf79813 | ||
|
33383a713a | ||
|
9a8c21dd94 | ||
|
0d8be4ed2c | ||
|
f745b442aa | ||
|
50b67ff7ab | ||
|
6c79aad7f8 | ||
|
2cda61b258 | ||
|
e3504dc9ea | ||
|
e35cc0358d | ||
|
8aab7476b8 | ||
|
35cd280b4e | ||
|
e573f44c64 | ||
|
78d84c7007 | ||
|
1b132e0f79 | ||
|
fbd08730aa | ||
|
d1f24cb315 | ||
|
f36fb27520 | ||
|
fbe4a8c049 | ||
|
a6518742fa | ||
|
6f5cca9b49 | ||
|
c4e2987d09 | ||
|
846c3dfb4e | ||
|
4e7e166741 | ||
|
63e205dbee | ||
|
ef40691ae0 | ||
|
859055145c | ||
|
5f82002927 | ||
|
abf0af9d0a | ||
|
7933868ed4 | ||
|
145db1e3d7 | ||
|
bc744a6fe1 | ||
|
5456694bb0 | ||
|
02b3421515 | ||
|
87431ce883 | ||
|
96f5eadb5f | ||
|
254fc15d16 | ||
|
bc5317aa93 | ||
|
c25250957e | ||
|
2402a28938 | ||
|
3ead5f8092 | ||
|
8f1a4209c4 | ||
|
776b6fa55a | ||
|
034f4bf0ac | ||
|
a6aaaa0a68 | ||
|
dff72b3073 | ||
|
3b48ece929 | ||
|
bcf63c83a7 | ||
|
322392cdae | ||
|
e1e41f325b | ||
|
74ec341e16 | ||
|
c517418e54 | ||
|
d9c6d1ae97 | ||
|
d290c3cf70 | ||
|
19f13f8c79 | ||
|
ad9db582c8 | ||
|
6abe138aa9 | ||
|
eafe2f8417 | ||
|
e1aa3951ea | ||
|
bd7d35d050 | ||
|
867cad5d7d | ||
|
e7a63a4d73 | ||
|
0dfb834044 | ||
|
cd9aef3b3f | ||
|
300164443f | ||
|
f74b530662 | ||
|
f352f70280 | ||
|
e57c287c48 | ||
|
29fd250ace |
37
.gitignore
vendored
|
@ -1,15 +1,26 @@
|
||||||
.DS_Store
|
# build output
|
||||||
node_modules
|
|
||||||
.svelte-kit
|
|
||||||
/package
|
|
||||||
src/routes/**/
|
|
||||||
src/routes/*.md
|
|
||||||
static/
|
|
||||||
build
|
build
|
||||||
.vercel_build_output/
|
static
|
||||||
.netlify/
|
.svelte-kit
|
||||||
.env.local
|
.netlify
|
||||||
.env.**.local
|
.vercel
|
||||||
myblog/urara/2022-06-12-appwrite.md
|
|
||||||
*.config.js
|
|
||||||
|
# dependencies
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# env
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# logs
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# macOS-specific files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# temp file
|
||||||
|
src/routes/**/+page.svelte.md
|
||||||
|
src/routes/**/+page.md
|
||||||
|
src/static
|
||||||
|
*.config.js
|
||||||
urara.js
|
urara.js
|
1
.npmrc
|
@ -1 +1,2 @@
|
||||||
engine-strict=true
|
engine-strict=true
|
||||||
|
strict-peer-dependencies=false
|
||||||
|
|
2
.nvmrc
|
@ -1 +1 @@
|
||||||
v18.4.0
|
v19.4.0
|
2
.vscode/settings.json
vendored
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"files.eol": "\n",
|
"files.eol": "\n",
|
||||||
"typescript.tsdk": "node_modules\\typescript\\lib",
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
"css.lint.unknownAtRules": "ignore",
|
"css.lint.unknownAtRules": "ignore",
|
||||||
"svelte.plugin.css.diagnostics.enable": false,
|
"svelte.plugin.css.diagnostics.enable": false,
|
||||||
"[html]": {
|
"[html]": {
|
||||||
|
|
|
@ -20,11 +20,11 @@ My Tech Blog, base on [Urara](https://github.com/importantimport/urara)
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- [ ] Note Page
|
- [ ] Note Page
|
||||||
- [ ] Archie Page
|
- [ ] Archvie Page
|
||||||
- [ ] Refactoring the atom feed format
|
- [ ] Refactoring the atom feed format
|
||||||
- [ ] NeoDB component
|
- [ ] NeoDB component
|
||||||
- [ ] ...
|
- [ ] ...
|
||||||
|
|
||||||
### License:
|
### License
|
||||||
|
|
||||||
[CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/)
|
© 2024 Sevi.C All Rights Reserved.
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
echo "————————1. Build locally————————"
|
echo "————————1. Build locally————————"
|
||||||
netlify build
|
netlify build
|
||||||
echo "————————2.Deploy test————————"
|
echo "————————2.Deploy————————————————"
|
||||||
netlify deploy
|
|
||||||
echo "————————3.Deploy————————"
|
|
||||||
netlify deploy --prod
|
netlify deploy --prod
|
|
@ -13,6 +13,7 @@ import { parse, join } from 'path'
|
||||||
import { visit } from 'unist-util-visit'
|
import { visit } from 'unist-util-visit'
|
||||||
import { toString } from 'mdast-util-to-string'
|
import { toString } from 'mdast-util-to-string'
|
||||||
import Slugger from 'github-slugger'
|
import Slugger from 'github-slugger'
|
||||||
|
import remarkFFF from 'remark-fff'
|
||||||
import remarkFootnotes from 'remark-footnotes'
|
import remarkFootnotes from 'remark-footnotes'
|
||||||
|
|
||||||
// highlighter
|
// highlighter
|
||||||
|
@ -24,12 +25,12 @@ type VALUE = { [key in string | number]: VALUE } | Array<VALUE> | string | boole
|
||||||
const remarkUraraFm =
|
const remarkUraraFm =
|
||||||
() =>
|
() =>
|
||||||
(tree: Node<Data>, { data, filename }: { data: { fm?: Record<string, unknown> }; filename?: string }) => {
|
(tree: Node<Data>, { data, filename }: { data: { fm?: Record<string, unknown> }; filename?: string }) => {
|
||||||
const filepath = (filename as string).split('/src/routes')[1]
|
const filepath = filename ? filename.split('/src/routes')[1] : 'unknown'
|
||||||
const { dir, name } = parse(filepath)
|
const { dir, name } = parse(filepath)
|
||||||
if (!data.fm) data.fm = {}
|
if (!data.fm) data.fm = {}
|
||||||
// Generate slug & path
|
// Generate slug & path
|
||||||
data.fm.slug = filepath
|
data.fm.slug = filepath
|
||||||
data.fm.path = join(dir, `/${name}`.replace('/index', '').replace('.svelte', ''))
|
data.fm.path = join(dir, `/${name}`.replace('/+page', '').replace('.svelte', ''))
|
||||||
// Generate ToC
|
// Generate ToC
|
||||||
if (data.fm.toc !== false) {
|
if (data.fm.toc !== false) {
|
||||||
const [slugs, toc]: [slugs: Slugger, toc: { depth: number; title: string; slug: string }[]] = [new Slugger(), []]
|
const [slugs, toc]: [slugs: Slugger, toc: { depth: number; title: string; slug: string }[]] = [new Slugger(), []]
|
||||||
|
@ -40,23 +41,9 @@ const remarkUraraFm =
|
||||||
slug: slugs.slug(toString(node), false)
|
slug: slugs.slug(toString(node), false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
data.fm.toc = toc
|
if (toc.length > 0) data.fm.toc = toc
|
||||||
|
else data.fm.toc = false
|
||||||
}
|
}
|
||||||
// Auto-read created & updated
|
|
||||||
if (!data.fm.created || !data.fm.updated) {
|
|
||||||
const { ctime, mtime } = statSync(new URL(`./urara${filepath}`, import.meta.url))
|
|
||||||
if (!data.fm.created) data.fm.created = ctime
|
|
||||||
if (!data.fm.updated) data.fm.updated = mtime
|
|
||||||
}
|
|
||||||
// Remark FFF Experimental (Hugo -> MDsveX)
|
|
||||||
Object.entries({
|
|
||||||
image: 'images',
|
|
||||||
tags: 'category',
|
|
||||||
bookmark_of: 'bookmarkOf',
|
|
||||||
like_of: 'likeOf',
|
|
||||||
repost_of: 'repostOf',
|
|
||||||
in_reply_to: 'inReplyTo'
|
|
||||||
}).forEach(([output, input]: string[]) => (data.fm = { ...data.fm, [output]: data.fm![input] }))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Better type definitions needed
|
// Better type definitions needed
|
||||||
|
@ -72,9 +59,7 @@ const remarkUraraSpoiler = () => (tree: Node<Data>) =>
|
||||||
return node
|
return node
|
||||||
})
|
})
|
||||||
|
|
||||||
const defineConfig = (config: MdsvexOptions) => config
|
export default {
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
extensions: ['.svelte.md', '.md'],
|
extensions: ['.svelte.md', '.md'],
|
||||||
smartypants: {
|
smartypants: {
|
||||||
dashes: 'oldschool'
|
dashes: 'oldschool'
|
||||||
|
@ -104,9 +89,24 @@ export default defineConfig({
|
||||||
)}\` }`
|
)}\` }`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
remarkPlugins: [remarkUraraFm, remarkUraraSpoiler, [remarkFootnotes, { inlineNotes: true }]],
|
remarkPlugins: [
|
||||||
|
[
|
||||||
|
remarkFFF as any,
|
||||||
|
{
|
||||||
|
presets: ['hugo'],
|
||||||
|
target: 'mdsvex',
|
||||||
|
autofill: {
|
||||||
|
provider: 'fs',
|
||||||
|
path: (path: string) => path.replace('/src/routes/', '/urara/')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
remarkUraraFm,
|
||||||
|
remarkUraraSpoiler,
|
||||||
|
[remarkFootnotes, { inlineNotes: true }]
|
||||||
|
],
|
||||||
rehypePlugins: [
|
rehypePlugins: [
|
||||||
rehypeSlug,
|
rehypeSlug as any,
|
||||||
[rehypeAutolinkHeadings, { behavior: 'wrap' }],
|
[rehypeAutolinkHeadings, { behavior: 'wrap' }],
|
||||||
[
|
[
|
||||||
rehypeExternalLinks,
|
rehypeExternalLinks,
|
||||||
|
@ -116,4 +116,4 @@ export default defineConfig({
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
})
|
} as MdsvexOptions
|
||||||
|
|
15
netlify.toml
|
@ -1,8 +1,7 @@
|
||||||
[build]
|
[build]
|
||||||
command = "npx pnpm i --store=node_modules/.pnpm-store && npx pnpm build"
|
command = "corepack prepare pnpm@8.5.1 --activate && pnpm i && npx pnpm build"
|
||||||
publish = "build"
|
publish = "build"
|
||||||
|
|
||||||
|
|
||||||
[build.environment]
|
[build.environment]
|
||||||
NPM_FLAGS = "--version"
|
NPM_FLAGS = "--version"
|
||||||
AWS_LAMBDA_JS_RUNTIME = "nodejs16.x"
|
AWS_LAMBDA_JS_RUNTIME = "nodejs16.x"
|
||||||
|
@ -26,13 +25,17 @@ node_bundler = "esbuild"
|
||||||
[[headers]]
|
[[headers]]
|
||||||
for = "/*"
|
for = "/*"
|
||||||
[headers.values]
|
[headers.values]
|
||||||
Access-Control-Allow-Origin = "https://seviche.cc"
|
Access-Control-Allow-Origin = "sevic.me"
|
||||||
X-Frame-Options = "DENY"
|
X-Frame-Options = "SAMEORIGIN"
|
||||||
X-Content-Type-Options = "nosniff"
|
X-Content-Type-Options = "nosniff"
|
||||||
X-XSS-Protection = "1; mode=block"
|
X-XSS-Protection = "1; mode=block"
|
||||||
Content-Security-Policy = "style-src 'self' 'unsafe-inline' https://cdn.commento.io/css/commento.css http://fonts.cdnfonts.com/css/lato ; script-src 'self' 'unsafe-inline' https://*.seviche.cc https://giscus.app https://hexoverc.vercel.app/umami.js https://cdn.splitbee.io/sb.js https://cdn.commento.io/js/commento.js"
|
Content-Security-Policy = "script-src 'self' 'unsafe-inline' https://*.seviche.cc https://giscus.app https://hexoverc.vercel.app/umami.js https://cdn.splitbee.io/sb.js https://plausiable.seviche.cc/js/script.js"
|
||||||
Referrer-Policy = "strict-origin-when-cross-origin"
|
Referrer-Policy = "strict-origin-when-cross-origin"
|
||||||
Permissions-Policy = "usb=()"
|
[[redirects]]
|
||||||
|
from = "/.well-known/webfinger"
|
||||||
|
to = "https://kongwoo.icu/.well-known/webfinger?resource=acct:blog@kongwoo.icu"
|
||||||
|
status = 302
|
||||||
|
force = true
|
||||||
|
|
||||||
[[redirects]]
|
[[redirects]]
|
||||||
from = "/.well-known/host-meta"
|
from = "/.well-known/host-meta"
|
||||||
|
|
104
package.json
|
@ -12,16 +12,12 @@
|
||||||
"tsc:watch": "tsc -w -p tsconfig.node.json",
|
"tsc:watch": "tsc -w -p tsconfig.node.json",
|
||||||
"urara:build": "node urara.js build",
|
"urara:build": "node urara.js build",
|
||||||
"urara:watch": "node urara.js watch",
|
"urara:watch": "node urara.js watch",
|
||||||
"kit:dev": "cross-env NODE_OPTIONS=--max_old_space_size=7680 vite dev",
|
"kit:dev": "cross-env NODE_OPTIONS=--max_old_space_size=11474 vite dev",
|
||||||
"kit:build": "cross-env NODE_OPTIONS=--max_old_space_size=7680 vite build",
|
"kit:build": "cross-env NODE_OPTIONS=--max_old_space_size=11474 vite build",
|
||||||
"dev:parallel": "npm-run-all -p -r tsc:watch urara:watch \"kit:dev {@} \" --",
|
"dev:parallel": "npm-run-all -p -r tsc:watch urara:watch \"kit:dev {@} \" --",
|
||||||
"dev": "npm-run-all -s tsc \"dev:parallel {@} \" --",
|
"dev": "npm-run-all -s tsc \"dev:parallel {@} \" --",
|
||||||
"build": "npm-run-all -s tsc urara:build kit:build clean",
|
"build": "npm-run-all -s tsc urara:build kit:build clean",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"dev:urara": "node urara.js watch",
|
|
||||||
"dev:kit": "export NODE_OPTIONS=--max_old_space_size=8192 && MODE=development svelte-kit dev",
|
|
||||||
"build:urara": "node urara.ts build",
|
|
||||||
"build:kit": "export NODE_OPTIONS=--max_old_space_size=8192 && svelte-kit build",
|
|
||||||
"check": "svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-check --tsconfig ./tsconfig.json",
|
||||||
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
|
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
"lint": "prettier --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
|
"lint": "prettier --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
|
||||||
|
@ -29,58 +25,66 @@
|
||||||
"zhlint": "zhlint urara/*/*.md --fix && zhlint urara/*.md --fix"
|
"zhlint": "zhlint urara/*/*.md --fix && zhlint urara/*.md --fix"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify-json/heroicons-outline": "^1.1.2",
|
"@iconify-json/heroicons-outline": "^1.1.6",
|
||||||
"@iconify-json/heroicons-solid": "^1.1.2",
|
"@iconify-json/heroicons-solid": "^1.1.7",
|
||||||
"@iconify-json/ic": "^1.1.9",
|
"@iconify-json/ic": "^1.1.13",
|
||||||
"@iconify-json/simple-icons": "1.1.21",
|
"@iconify-json/icon-park-twotone": "1.1.9",
|
||||||
"@iconify-json/material-symbols": "1.1.14",
|
"@iconify-json/material-symbols": "1.1.41",
|
||||||
"@iconify-json/mdi": "^1.1.30",
|
"@iconify-json/mdi": "^1.1.52",
|
||||||
"@iconify-json/uil": "^1.1.2",
|
"@iconify-json/simple-icons": "1.1.52",
|
||||||
"@sveltejs/adapter-auto": "1.0.0-next.64",
|
"@iconify-json/uil": "^1.1.4",
|
||||||
"@sveltejs/adapter-node": "1.0.0-next.86",
|
"@sveltejs/adapter-netlify": "^2.0.7",
|
||||||
"@sveltejs/adapter-static": "1.0.0-next.39",
|
"@sveltejs/adapter-static": "^2.0.2",
|
||||||
"@sveltejs/kit": "1.0.0-next.405",
|
"@sveltejs/adapter-vercel": "2.4.3",
|
||||||
"@tailwindcss/typography": "^0.5.4",
|
"@sveltejs/kit": "^1.19.0",
|
||||||
"@types/node": "^18.7.8",
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
|
"@types/node": "^20.2.5",
|
||||||
"@types/unist": "^2.0.6",
|
"@types/unist": "^2.0.6",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.33.1",
|
"@typescript-eslint/eslint-plugin": "^5.59.7",
|
||||||
"@typescript-eslint/parser": "^5.33.1",
|
"@typescript-eslint/parser": "^5.59.7",
|
||||||
"autoprefixer": "^10.4.8",
|
"@unocss/extractor-svelte": "^0.51.13",
|
||||||
"chalk": "^5.0.1",
|
"@vite-pwa/sveltekit": "^0.1.3",
|
||||||
|
"chalk": "^5.2.0",
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"cssnano": "^5.1.13",
|
"daisyui": "^2.51.6",
|
||||||
"daisyui": "^2.24.0",
|
"eslint": "^8.41.0",
|
||||||
"eslint": "^8.22.0",
|
"eslint-config-prettier": "^8.8.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
|
||||||
"eslint-plugin-svelte3": "^4.0.0",
|
"eslint-plugin-svelte3": "^4.0.0",
|
||||||
"fenceparser": "^2.2.0",
|
"fenceparser": "^2.2.0",
|
||||||
"fff-flavored-frontmatter": "~0.2.2",
|
"fff-flavored-frontmatter": "~0.5.3",
|
||||||
"github-slugger": "^1.4.0",
|
"github-slugger": "^2.0.0",
|
||||||
"mdast-util-to-string": "^3.1.0",
|
"mdast-util-to-string": "^3.2.0",
|
||||||
"mdsvex": "^0.10.6",
|
"mdsvex": "^0.10.6",
|
||||||
|
"netlify-cli": "^16.3.1",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"postcss": "^8.4.16",
|
"postcss": "^8.4.23",
|
||||||
"prettier": "^2.7.1",
|
"postcss-lightningcss": "^0.7.0",
|
||||||
"prettier-plugin-svelte": "^2.7.0",
|
"prettier": "^2.8.8",
|
||||||
|
"prettier-plugin-svelte": "^2.10.0",
|
||||||
"rehype-autolink-headings": "^6.1.1",
|
"rehype-autolink-headings": "^6.1.1",
|
||||||
"rehype-external-links": "^2.0.0",
|
"rehype-external-links": "^2.1.0",
|
||||||
"rehype-slug": "^5.0.1",
|
"rehype-slug": "^5.1.0",
|
||||||
"remark": "^14.0.2",
|
"remark": "^14.0.3",
|
||||||
|
"remark-fff": "~0.5.3",
|
||||||
"remark-footnotes": "~2.0.0",
|
"remark-footnotes": "~2.0.0",
|
||||||
"shiki-twoslash": "^3.1.0",
|
"rollup": "^3.23.0",
|
||||||
"svelte": "^3.49.0",
|
"shiki-twoslash": "^3.1.2",
|
||||||
|
"svelte": "^3.59.1",
|
||||||
"svelte-bricks": "^0.1.7",
|
"svelte-bricks": "^0.1.7",
|
||||||
"svelte-check": "^2.8.1",
|
"svelte-check": "^3.4.3",
|
||||||
"svelte-preprocess": "^4.10.7",
|
"svelte-preprocess": "^5.0.4",
|
||||||
"svelte-typeahead": "^4.2.4",
|
"svelte-typeahead": "^4.4.1",
|
||||||
"tailwindcss": "^3.1.8",
|
"sveltekit-embed": "^0.0.12",
|
||||||
"tslib": "^2.4.0",
|
"tailwindcss": "^3.3.2",
|
||||||
"typescript": "^4.7.4",
|
"tslib": "^2.5.2",
|
||||||
"unist-util-visit": "^4.1.0",
|
"typescript": "^5.0.4",
|
||||||
"unocss": "^0.45.8",
|
"unist-util-visit": "^4.1.2",
|
||||||
"vite": "^3.0.9",
|
"unocss": "^0.51.13",
|
||||||
"vite-plugin-pwa": "^0.12.3",
|
"vite": "^4.3.9",
|
||||||
"workbox-window": "^6.5.4"
|
"vite-imagetools": "^4.0.19",
|
||||||
|
"vite-plugin-pwa": "^0.14.7",
|
||||||
|
"workbox-build": "^6.6.0",
|
||||||
|
"workbox-window": "^6.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
17289
pnpm-lock.yaml
11
src/app.d.ts
vendored
|
@ -1,8 +1,9 @@
|
||||||
/// <reference types="@sveltejs/kit" />
|
/// <reference types="@sveltejs/kit" />
|
||||||
|
|
||||||
import { FFFBase, FFFExtra } from 'fff-flavored-frontmatter'
|
import type { FFFBase, FFFMention } from 'fff-flavored-frontmatter'
|
||||||
|
|
||||||
interface ImportMetaEnv extends Readonly<Record<string, string>> {
|
interface ImportMetaEnv extends Readonly<Record<string, string>> {
|
||||||
|
readonly URARA_SITE_PROTOCOL?: 'http://' | 'https://'
|
||||||
readonly URARA_SITE_DOMAIN?: string
|
readonly URARA_SITE_DOMAIN?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,8 +15,8 @@ interface ImportMeta {
|
||||||
declare global {
|
declare global {
|
||||||
namespace Urara {
|
namespace Urara {
|
||||||
namespace Post {
|
namespace Post {
|
||||||
type Frontmatter = Omit<FFFBase, 'created' | 'updated' | 'image' | 'audio' | 'video' | 'flags'> &
|
type Frontmatter = Omit<FFFBase, 'flags'> &
|
||||||
Pick<FFFExtra, 'in_reply_to'> & {
|
Pick<FFFMention, 'in_reply_to'> & {
|
||||||
/**
|
/**
|
||||||
* post type.
|
* post type.
|
||||||
* @remarks auto-generated
|
* @remarks auto-generated
|
||||||
|
@ -50,6 +51,10 @@ declare global {
|
||||||
* @remarks auto-generated or set manually
|
* @remarks auto-generated or set manually
|
||||||
*/
|
*/
|
||||||
updated: string
|
updated: string
|
||||||
|
/**
|
||||||
|
* the published date of the post.
|
||||||
|
*/
|
||||||
|
published?: string
|
||||||
/**
|
/**
|
||||||
* the featured image for article, or image for "photo" / "multi-photo" posts.
|
* the featured image for article, or image for "photo" / "multi-photo" posts.
|
||||||
* @remarks currently only supports string
|
* @remarks currently only supports string
|
||||||
|
|
|
@ -13,5 +13,6 @@
|
||||||
|
|
||||||
<body itemscope itemtype="https://schema.org/WebPage">
|
<body itemscope itemtype="https://schema.org/WebPage">
|
||||||
%sveltekit.body%
|
%sveltekit.body%
|
||||||
|
<div style="display: contents">%sveltekit.body%</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -56,7 +56,12 @@ html {
|
||||||
/* .urara-prose a */
|
/* .urara-prose a */
|
||||||
|
|
||||||
.urara-prose :is(p, li) > a {
|
.urara-prose :is(p, li) > a {
|
||||||
@apply bg-[length:100%_0.2em] hover:bg-[length:100%_100%] bg-[position:0_88%] bg-gradient-to-t from-secondary/50 to-secondary/40 bg-no-repeat transition-all ease-in-out !no-underline;
|
@apply underline
|
||||||
|
font-normal
|
||||||
|
hover:decoration-red-700/50
|
||||||
|
hover:text-red-700
|
||||||
|
decoration-2
|
||||||
|
underline-offset-4 decoration-gray-700/30;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* .urara-prose misc */
|
/* .urara-prose misc */
|
||||||
|
@ -188,9 +193,9 @@ pre.twoslash data-lsp:hover::before {
|
||||||
|
|
||||||
.urara-prose blockquote:before {
|
.urara-prose blockquote:before {
|
||||||
vertical-align: -0.4em;
|
vertical-align: -0.4em;
|
||||||
@apply mr-2 text-5xl leading-3 italic font-serif opacity-25;
|
@apply mr-2 text-5xl mt-4 leading-3 italic font-serif opacity-25 float-left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.urara-prose blockquote p {
|
.urara-prose blockquote p {
|
||||||
@apply inline opacity-80 before:content-[''] after:content-[''];
|
@apply opacity-80 before:content-[''] after:content-[''];
|
||||||
}
|
}
|
7
src/hooks.server.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import type { Handle } from '@sveltejs/kit'
|
||||||
|
import { site } from '$lib/config/site'
|
||||||
|
|
||||||
|
export const handle: Handle = async ({ event, resolve }) =>
|
||||||
|
await resolve(event, {
|
||||||
|
transformPageChunk: ({ html }) => html.replace('<html lang="en">', `<html lang="${site.lang ?? 'en'}">`)
|
||||||
|
})
|
5
src/lib/components/actions/1-comment.svelte
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<button class="tooltip tooltip-left opacity-60 hover:opacity-100" data-tip="Jump to comments">
|
||||||
|
<a href="#post-comment" class="btn btn-circle btn-ghost hover:bg-red-10">
|
||||||
|
<span class="i-heroicons-outline-chat-alt-2" />
|
||||||
|
</a>
|
||||||
|
</button>
|
|
@ -1,3 +0,0 @@
|
||||||
<a href="#post-comment" class="btn btn-lg btn-circle btn-ghost bg-base-100 shadow-lg hover:shadow-xl">
|
|
||||||
<span class="i-heroicons-outline-chat-alt-2" />
|
|
||||||
</a>
|
|
|
@ -3,10 +3,12 @@
|
||||||
export let post: Urara.Post
|
export let post: Urara.Post
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<a
|
<button class="tooltip tooltip-left opacity-60 hover:opacity-100" data-tip="Translate">
|
||||||
href={`https://translate.google.com/translate?sl=auto&tl=${
|
<a
|
||||||
navigator.languages ? navigator.languages[0] : navigator.language
|
href={`https://translate.google.com/translate?sl=auto&tl=${
|
||||||
}&u=${site.protocol + site.domain + post.path}`}
|
navigator.languages ? navigator.languages[0] : navigator.language
|
||||||
class="btn btn-lg btn-circle btn-ghost bg-base-100 shadow-lg hover:shadow-xl">
|
}&u=${site.protocol + site.domain + post.path}`}
|
||||||
<span class="i-heroicons-outline-translate" />
|
class="btn btn-circle btn-ghost">
|
||||||
</a>
|
<span class="i-heroicons-outline-translate" />
|
||||||
|
</a>
|
||||||
|
</button>
|
||||||
|
|
|
@ -3,10 +3,12 @@
|
||||||
export let post: Urara.Post
|
export let post: Urara.Post
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<a
|
<button class="tooltip tooltip-left opacity-60 hover:opacity-100" data-tip="Share">
|
||||||
href={`https://www.addtoany.com/share#url=${site.protocol + site.domain + post.path}&title=${encodeURI(
|
<a
|
||||||
post.title ?? post.path.slice(1)
|
href={`https://www.addtoany.com/share#url=${site.protocol + site.domain + post.path}&title=${encodeURI(
|
||||||
)}`}
|
post.title ?? post.path.slice(1)
|
||||||
class="btn btn-lg btn-circle btn-ghost bg-base-100 shadow-lg hover:shadow-xl">
|
)}`}
|
||||||
<span class="i-heroicons-outline-share" />
|
class="btn btn-circle btn-ghost">
|
||||||
</a>
|
<span class="i-heroicons-outline-share" />
|
||||||
|
</a>
|
||||||
|
</button>
|
||||||
|
|
63
src/lib/components/comments/remark42.svelte
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { onMount, onDestroy } from 'svelte'
|
||||||
|
import type { Remark42Config } from '$lib/types/post'
|
||||||
|
export let post: Urara.Post
|
||||||
|
export let config: Remark42Config
|
||||||
|
|
||||||
|
let remark42Instance: any
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
const [c, s] = [document.createElement('script'), document.createElement('script')]
|
||||||
|
|
||||||
|
c.id = 'remark42_config'
|
||||||
|
c.type = 'application/javascript'
|
||||||
|
c.innerHTML = `
|
||||||
|
var remark_config = {
|
||||||
|
host: '${config.host}',
|
||||||
|
site_id: '${config.site_id || 'remark'}',
|
||||||
|
url: '${post.path}',
|
||||||
|
components: [${config.components || "'embed'"}],
|
||||||
|
max_shown_comments: ${config.max_shown_comments || 15},
|
||||||
|
max_last_comments: ${config.max_last_comments || 15},
|
||||||
|
theme: '${config.theme || 'light'}',
|
||||||
|
page_title: '${config.page_title || post.title}',
|
||||||
|
locale: '${config.locale || 'en'}',
|
||||||
|
show_email_subscription: ${config.show_email_subscription || true},
|
||||||
|
show_rss_subscription: ${config.show_rss_subscription || true},
|
||||||
|
simple_view: ${config.simple_view || false},
|
||||||
|
no_footer: ${config.no_footer || false},
|
||||||
|
}`
|
||||||
|
|
||||||
|
s.id = 'remark42_script'
|
||||||
|
s.type = 'application/javascript'
|
||||||
|
s.innerHTML = `!function(e,n){for(var o=0;o<e.length;o++){var r=n.createElement("script"),c=".js",d=n.head||n.body;"noModule"in r?(r.type="module",c=".mjs"):r.async=!0,r.defer=!0,r.src='${config.host}/web/'+e[o]+c,d.appendChild(r)}}(remark_config.components||["embed"],document);`
|
||||||
|
document.head.appendChild(c)
|
||||||
|
document.head.appendChild(s)
|
||||||
|
|
||||||
|
const opt = {
|
||||||
|
...config,
|
||||||
|
url: post.path
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkRemark42 = () => {
|
||||||
|
if ((window as any).REMARK42) {
|
||||||
|
remark42Instance = (window as any).REMARK42.createInstance({
|
||||||
|
node: document.getElementById('remark42') as HTMLElement,
|
||||||
|
...opt
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setTimeout(checkRemark42, 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkRemark42()
|
||||||
|
})
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
if (remark42Instance && typeof remark42Instance.destroy === 'function') {
|
||||||
|
remark42Instance.destroy()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div id="remark42" />
|
|
@ -1,117 +0,0 @@
|
||||||
<!-- <script lang="ts">
|
|
||||||
import { onMount } from 'svelte'
|
|
||||||
import { site } from '$lib/config/site'
|
|
||||||
import type { WalineConfig } from '$lib/types/post'
|
|
||||||
|
|
||||||
export let post: Urara.Post
|
|
||||||
export let config: WalineConfig
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
const waline = document.createElement('script')
|
|
||||||
const [c, s] = [document.createElement('script'), document.createElement('script')]
|
|
||||||
c.id = 'disqus_config'
|
|
||||||
c.type = 'application/javascript'
|
|
||||||
console.log(waline)
|
|
||||||
Object.entries({
|
|
||||||
src: 'https://unpkg.com/@waline/client@v2/dist/waline.js',
|
|
||||||
serverURL: config.serverURL,
|
|
||||||
path: config.path ?? post.path ?? window.location.pathname,
|
|
||||||
lang: config.lang ?? 'en',
|
|
||||||
emoji: config.emoji ?? ['//unpkg.com/@waline/emojis@1.0.1/weibo'],
|
|
||||||
dark: config.dark ?? false,
|
|
||||||
meta: config.meta ?? ['nick', 'mail', 'link'],
|
|
||||||
requiredMeta: config.requiredMeta ?? [],
|
|
||||||
login: config.login ?? 'enable',
|
|
||||||
wordLimit: config.wordLimit ?? 0,
|
|
||||||
pageSize: config.pageSize ?? 10,
|
|
||||||
imageUploader: config.imageUploader,
|
|
||||||
highlighter: config.highlighter,
|
|
||||||
texRender: config.texRender,
|
|
||||||
copyright: config.copyright ?? true,
|
|
||||||
crossorigin: 'anonymous',
|
|
||||||
async: ''
|
|
||||||
}).forEach(([key, value]) => waline.setAttribute(key, value))
|
|
||||||
setTimeout(() => {
|
|
||||||
const observer = new MutationObserver(() => {
|
|
||||||
document.getElementById('giscus-loading').remove()
|
|
||||||
observer.disconnect()
|
|
||||||
})
|
|
||||||
observer.observe(document.getElementById('giscus'), {
|
|
||||||
childList: true
|
|
||||||
})
|
|
||||||
document.getElementById('giscus-container').appendChild(waline)
|
|
||||||
}, 1000)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://unpkg.com/@waline/client@v2/dist/waline.css" />
|
|
||||||
<div id="waline" class="waline-container" />
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.waline-container {
|
|
||||||
background-color: var(--card-background);
|
|
||||||
border-radius: var(--card-border-radius);
|
|
||||||
box-shadow: var(--shadow-l1);
|
|
||||||
padding: 2%;
|
|
||||||
}
|
|
||||||
.waline-container .vcount {
|
|
||||||
color: var(--card-text-color-main);
|
|
||||||
}
|
|
||||||
.v[data-class='v'] .vcard {
|
|
||||||
flex: 1;
|
|
||||||
width: 0;
|
|
||||||
padding-bottom: 0.5em;
|
|
||||||
border-bottom: 0; /*删掉回复下面的线*/
|
|
||||||
}
|
|
||||||
.v[data-class='v'] .vcard .vquote {
|
|
||||||
border-left: 1px solid rgba(237, 237, 237, 0.5);
|
|
||||||
}
|
|
||||||
@media (max-width: 580px) {
|
|
||||||
.v[data-class='v'] .vheader .vheader-item:not(:last-child) {
|
|
||||||
border-bottom: 1px solid rgba(237, 237, 237, 0.8); /*输入框分割线*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*日间模式*/
|
|
||||||
:root {
|
|
||||||
--waline-theme-color: #34495e; /*主题色,提交按钮*/
|
|
||||||
--waline-active-color: #246bb1; /*鼠标移到提交按钮上的颜色*/
|
|
||||||
/* 徽章 */
|
|
||||||
--waline-badge-color: #34495e; /*博主徽章色*/
|
|
||||||
--waline-avatar-radius: 5px;
|
|
||||||
--waline-avatar-size: 6rem;
|
|
||||||
--waline-dark-grey: #34495e; /*ID颜色*/
|
|
||||||
--waline-text-color: #34495e; /*字体颜色*/
|
|
||||||
--waline-font-size: 1.7rem; /*字体大小颜色*/
|
|
||||||
}
|
|
||||||
/*夜间模式*/
|
|
||||||
:root[data-scheme='dark'] {
|
|
||||||
--waline-theme-color: #acc6e0;
|
|
||||||
--waline-white: #34495e; /*按键字体颜色*/
|
|
||||||
--waline-active-color: #8ab1d8;
|
|
||||||
--waline-light-grey: #666;
|
|
||||||
--waline-dark-grey: #acc6e0; /*ID颜色*/
|
|
||||||
--waline-badge-color: #acc6e0;
|
|
||||||
/* 布局颜色 */
|
|
||||||
--waline-text-color: rgba(255, 255, 255, 0.7);
|
|
||||||
--waline-bgcolor: #515151;
|
|
||||||
--waline-bgcolor-light: #66696b; /*行内代码块颜色*/
|
|
||||||
--waline-border-color: #9b9c9c;
|
|
||||||
--waline-disable-bgcolor: #444;
|
|
||||||
--waline-disable-color: #272727;
|
|
||||||
/* 特殊颜色 */
|
|
||||||
--waline-bq-color: #9b9c9c; /*quote*/
|
|
||||||
/* 其他颜色 */
|
|
||||||
--waline-info-bgcolor: #acc6e0;
|
|
||||||
--waline-info-color: #9b9c9c;
|
|
||||||
}
|
|
||||||
.v[data-class='v'] .vcontent .vemoji {
|
|
||||||
width: 2.2em; /*表情包大小修改*/
|
|
||||||
margin: 0.25em;
|
|
||||||
}
|
|
||||||
.v[data-class='v'] .vheader {
|
|
||||||
border-bottom: 1px solid rgba(237, 237, 237, 0.8); /*输入框分割线*/
|
|
||||||
}
|
|
||||||
.v[data-class='v'] .vpanel {
|
|
||||||
border-radius: 8px; /*输入框圆角*/
|
|
||||||
}
|
|
||||||
</style> -->
|
|
|
@ -74,6 +74,36 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col gap-8">
|
<div class="flex flex-col gap-8">
|
||||||
|
{#if config?.form === true}
|
||||||
|
<form id="webmention-form" method="post" action="https://webmention.io/{config.username}/webmention">
|
||||||
|
<input type="hidden" name="target" value={site.protocol + site.domain + post.path} />
|
||||||
|
<div class="label gap-4">
|
||||||
|
<span class="label-text">send webmentions here:</span>
|
||||||
|
<!-- {#if config?.commentParade === true}
|
||||||
|
<span class="label-text-alt text-right">
|
||||||
|
or <a
|
||||||
|
class="hover:!text-primary"
|
||||||
|
href="https://quill.p3k.io/?dontask=1&me=https://commentpara.de/&reply={encodeURI(
|
||||||
|
site.protocol + site.domain + post.path
|
||||||
|
)}">
|
||||||
|
comment anonymously
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
{/if} -->
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<div class="flex-1">
|
||||||
|
<input
|
||||||
|
class="input input-bordered focus:input-primary w-full"
|
||||||
|
type="text"
|
||||||
|
id="reply-url"
|
||||||
|
name="source"
|
||||||
|
placeholder="https://example.com/my-post" />
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary flex-none mt-auto" type="submit" id="webmention-submit">Send</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{/if}
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<p class="flex-1 m-auto italic opacity-50">
|
<p class="flex-1 m-auto italic opacity-50">
|
||||||
<!-- {`Sort by=${config?.sortBy ?? 'Created'}&sort-dir=${sortDirUp ? 'up' : 'down'}`} -->
|
<!-- {`Sort by=${config?.sortBy ?? 'Created'}&sort-dir=${sortDirUp ? 'up' : 'down'}`} -->
|
||||||
|
@ -172,34 +202,4 @@
|
||||||
{:else}
|
{:else}
|
||||||
<button id="webmention-loading" class="btn btn-lg btn-block flex btn-ghost loading" />
|
<button id="webmention-loading" class="btn btn-lg btn-block flex btn-ghost loading" />
|
||||||
{/if}
|
{/if}
|
||||||
{#if config?.form === true}
|
|
||||||
<form id="webmention-form" method="post" action="https://webmention.io/{config.username}/webmention">
|
|
||||||
<input type="hidden" name="target" value={site.protocol + site.domain + post.path} />
|
|
||||||
<div class="label gap-4">
|
|
||||||
<span class="label-text">send webmentions here:</span>
|
|
||||||
{#if config?.commentParade === true}
|
|
||||||
<span class="label-text-alt text-right">
|
|
||||||
or <a
|
|
||||||
class="hover:!text-primary"
|
|
||||||
href="https://quill.p3k.io/?dontask=1&me=https://commentpara.de/&reply={encodeURI(
|
|
||||||
site.protocol + site.domain + post.path
|
|
||||||
)}">
|
|
||||||
comment anonymously
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-2">
|
|
||||||
<div class="flex-1">
|
|
||||||
<input
|
|
||||||
class="input input-bordered focus:input-primary w-full"
|
|
||||||
type="text"
|
|
||||||
id="reply-url"
|
|
||||||
name="source"
|
|
||||||
placeholder="https://example.com/my-post" />
|
|
||||||
</div>
|
|
||||||
<button class="btn btn-primary flex-none mt-auto" type="submit" id="webmention-submit">Send</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
.then(
|
.then(
|
||||||
({ links }) => links.find((link: { rel: string }) => link.rel === 'http://ostatus.org/schema/1.0/subscribe').template
|
({ links }) => links.find((link: { rel: string }) => link.rel === 'http://ostatus.org/schema/1.0/subscribe').template
|
||||||
)
|
)
|
||||||
.then(template => (window.location.href = template.replace('{uri}', `sevichecc@kongwoo.icu`)))
|
.then(template => (window.location.href = template.replace('{uri}', `blog@seviche.cc`)))
|
||||||
.catch(error => console.error(error))
|
.catch(error => console.error(error))
|
||||||
$: if (input)
|
$: if (input)
|
||||||
input.length < 5 ? (status = '') : input.includes('@') && input.includes('.') ? (status = 'success') : (status = 'warning')
|
input.length < 5 ? (status = '') : input.includes('@') && input.includes('.') ? (status = 'success') : (status = 'warning')
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let user = undefined
|
export let user = ''
|
||||||
export let repo = undefined
|
export let repo = ''
|
||||||
let info: {
|
let info: {
|
||||||
html_url: string
|
html_url: string
|
||||||
description: string
|
description: string
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="card bg-base-100 !bg-base-200 my-4 ">
|
<div class="card bg-base-100 !bg-base-200 my-4">
|
||||||
<div class="p-6">
|
<div class="p-6">
|
||||||
{#key info}
|
{#key info}
|
||||||
{#if info}
|
{#if info}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { dev } from '$app/env'
|
import { dev } from '$app/environment'
|
||||||
let className = undefined
|
let className = undefined
|
||||||
export { className as class }
|
export { className as class }
|
||||||
export let src = undefined
|
export let src = undefined
|
||||||
|
|
|
@ -19,10 +19,10 @@
|
||||||
<div class="rounded-full border-2 border-white shadow-xl w-16 h-16">
|
<div class="rounded-full border-2 border-white shadow-xl w-16 h-16">
|
||||||
<img
|
<img
|
||||||
class="hover:rotate-[360deg] transition-transform duration-1000 ease-in-out m-0"
|
class="hover:rotate-[360deg] transition-transform duration-1000 ease-in-out m-0"
|
||||||
src={avatar ?? site.author.avatar}
|
|
||||||
alt={name ?? site.author.name}
|
alt={name ?? site.author.name}
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
decoding="async" />
|
decoding="async"
|
||||||
|
src={avatar ?? site.author.avatar} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if subname}
|
{#if subname}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<div class="card-body p-4">
|
<div class="card-body p-4">
|
||||||
<div class="flex flex-col md:flex-row items-start gap-4">
|
<div class="flex flex-col md:flex-row items-start gap-4">
|
||||||
<div class="mb-auto aspect-video w-full max-w-full shrink-0 md:max-w-xs">
|
<div class="mb-auto aspect-video w-full max-w-full shrink-0 md:max-w-xs">
|
||||||
<img class="rounded-md " src={project.img} alt={project.description} />
|
<img class="rounded-md" src={project.img} alt={project.description} />
|
||||||
</div>
|
</div>
|
||||||
<div class="card-title flex-1 flex-col items-start gap-4">
|
<div class="card-title flex-1 flex-col items-start gap-4">
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -1,22 +1,26 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let id = undefined
|
export let id: string
|
||||||
export let list = undefined
|
export let list: boolean | undefined = undefined
|
||||||
export let playlist = undefined
|
export let playlist: string | undefined = undefined
|
||||||
export let start = undefined
|
export let start: string | undefined = undefined
|
||||||
export let autoplay = false
|
export let autoplay: boolean = false
|
||||||
export let disablekb = false
|
export let disablekb: boolean = false
|
||||||
export let controls = true
|
export let controls: boolean = true
|
||||||
export let fs = true
|
export let fs = true
|
||||||
export let loop = false
|
export let loop = false
|
||||||
|
const src = `https://www.youtube.com/embed/${id}?${new URLSearchParams({
|
||||||
const src = `https://www.youtube.com/embed/${id}?${list ? `listType=playlist&list=${list}&` : ''}${
|
...(list ? { listType: 'playlist', list: 'true' } : {}),
|
||||||
playlist ? `playlist=${playlist}&` : ''
|
...(playlist ? { playlist } : {}),
|
||||||
}${start ? `start=${start}` : ''}${autoplay ? 'autoplay=1&' : ''}${disablekb ? 'disablekb=1&' : ''}${
|
...(start ? { start } : {}),
|
||||||
controls ? '' : 'controls=0&'
|
autoplay: autoplay ? '1' : '0',
|
||||||
}${fs ? '' : 'fs=0&'}${loop ? 'loop=1' : ''}`
|
disablekb: disablekb ? '1' : '0',
|
||||||
|
controls: controls ? '1' : '0',
|
||||||
|
fs: fs ? '1' : '0',
|
||||||
|
loop: loop ? '1' : '0'
|
||||||
|
}).toString()}`
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="relative pb-[56.25%] mb-2">
|
<div class="relative pb-[56.25%] mb-4">
|
||||||
<iframe
|
<iframe
|
||||||
{src}
|
{src}
|
||||||
class="absolute w-full h-full"
|
class="absolute w-full h-full"
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<p>
|
<p>
|
||||||
{#if footerConfig.nav}
|
{#if footerConfig.nav}
|
||||||
{#each footerConfig.nav as { text, link }, i}
|
{#each footerConfig.nav as { text, link }, i}
|
||||||
<a href={link} rel="noopener external" target="_blank">{text}</a>
|
<a href={link} rel="noopener noreferrer external" target="_blank">{text}</a>
|
||||||
{#if i + 1 < footerConfig.nav.length}
|
{#if i + 1 < footerConfig.nav.length}
|
||||||
<span class="mr-1">·</span>
|
<span class="mr-1">·</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
<br />
|
<br />
|
||||||
Powered by
|
Powered by
|
||||||
<a
|
<a
|
||||||
rel="noopener external"
|
rel="noopener noreferrer external"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="tooltip tooltip-secondary hover:text-secondary"
|
class="tooltip tooltip-secondary hover:text-secondary"
|
||||||
data-tip="🌸 [δ] - Based on MDsveX & SvelteKit 🌸"
|
data-tip="🌸 [δ] - Based on MDsveX & SvelteKit 🌸"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { dev } from '$app/env'
|
import { dev } from '$app/environment'
|
||||||
import { head } from '$lib/config/general'
|
import { head } from '$lib/config/general'
|
||||||
import { site } from '$lib/config/site'
|
import { site } from '$lib/config/site'
|
||||||
import OpenGraph from '$lib/components/head_opengraph.svelte'
|
import OpenGraph from '$lib/components/head_opengraph.svelte'
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { browser, dev } from '$app/env'
|
import { browser, dev } from '$app/environment'
|
||||||
import { fly } from 'svelte/transition'
|
import { fly } from 'svelte/transition'
|
||||||
import { site } from '$lib/config/site'
|
import { site } from '$lib/config/site'
|
||||||
import { theme } from '$lib/config/general'
|
import { theme } from '$lib/config/general'
|
||||||
|
@ -41,7 +41,8 @@
|
||||||
|
|
||||||
if (browser)
|
if (browser)
|
||||||
currentTheme =
|
currentTheme =
|
||||||
localStorage.getItem('theme') ?? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'night' : 'lemonade')
|
localStorage.getItem('theme') ??
|
||||||
|
(window.matchMedia('(prefers-color-scheme: dark)').matches ? theme?.[1].name : theme[0].name ?? theme[0].name)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
@ -54,60 +55,66 @@
|
||||||
id="header"
|
id="header"
|
||||||
class:-translate-y-32={!pin && scrollY > 0}
|
class:-translate-y-32={!pin && scrollY > 0}
|
||||||
class="fixed z-50 w-screen transition-all duration-500 ease-in-out border-b-2 border-transparent max-h-[4.125rem] {scrollY >
|
class="fixed z-50 w-screen transition-all duration-500 ease-in-out border-b-2 border-transparent max-h-[4.125rem] {scrollY >
|
||||||
32 && 'backdrop-blur border-base-content/10 bg-base-100/30 md:bg-base-200/30'}">
|
32 && 'backdrop-blur !border-base-content/10 bg-base-100/30 md:bg-base-200/30'}">
|
||||||
<div in:fly={{ x: -50, duration: 300, delay: 300 }} out:fly={{ x: -50, duration: 300 }} class="navbar">
|
{#if !search}
|
||||||
<div class="navbar-start">
|
<div in:fly={{ x: -50, duration: 300, delay: 300 }} out:fly={{ x: -50, duration: 300 }} class="navbar">
|
||||||
{#if headerConfig.nav}
|
<div class="navbar-start">
|
||||||
<Nav {path} {title} {pin} {scrollY} nav={headerConfig.nav} />
|
{#if headerConfig.nav}
|
||||||
{/if}
|
<Nav {path} {title} {pin} {scrollY} nav={headerConfig.nav} />
|
||||||
<a href="/" sveltekit:prefetch class="btn btn-ghost normal-case text-lg">{site.title}</a>
|
{/if}
|
||||||
</div>
|
<a href="/" class="btn btn-ghost normal-case text-lg">{site.title}</a>
|
||||||
<div class="navbar-end">
|
</div>
|
||||||
<!-- {#if headerConfig.search} -->
|
<div class="navbar-end">
|
||||||
<!-- The button to open modal -->
|
{#if headerConfig.search}
|
||||||
<!-- <label for="search-modal" class="btn btn-square btn-ghost ml-2"><span class="i-heroicons-outline-search" /></label> -->
|
<button aria-label="search" on:click={() => (search = !search)} tabindex="0" class="btn btn-square btn-ghost">
|
||||||
<!-- <button
|
|
||||||
on:click={() => {
|
|
||||||
search = !search
|
|
||||||
}}
|
|
||||||
type="submit"
|
|
||||||
class="btn btn-square btn-ghost ml-2">
|
|
||||||
<span class="i-heroicons-outline-search" />
|
<span class="i-heroicons-outline-search" />
|
||||||
</button> -->
|
</button>
|
||||||
<!-- {/if} -->
|
{/if}
|
||||||
<div id="change-theme" class="dropdown dropdown-end">
|
<div id="change-theme" class="dropdown dropdown-end">
|
||||||
<div tabindex="0" class="btn btn-square btn-ghost">
|
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||||
<span class="i-heroicons-outline-color-swatch" />
|
<!-- reference: https://github.com/saadeghi/daisyui/issues/1285 -->
|
||||||
|
<div tabindex="0" class="btn btn-square btn-ghost">
|
||||||
|
<span class="i-heroicons-outline-color-swatch" />
|
||||||
|
</div>
|
||||||
|
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||||
|
<!-- reference: https://github.com/saadeghi/daisyui/issues/1285 -->
|
||||||
|
<ul
|
||||||
|
tabindex="0"
|
||||||
|
class="flex flex-nowrap shadow-2xl menu dropdown-content bg-base-100 text-base-content rounded-box w-52 p-2 gap-2 overflow-y-auto max-h-[21.5rem]"
|
||||||
|
class:hidden={!pin}>
|
||||||
|
{#each theme as { name, text }}
|
||||||
|
<button
|
||||||
|
data-theme={name}
|
||||||
|
on:click={() => {
|
||||||
|
currentTheme = name
|
||||||
|
localStorage.setItem('theme', name)
|
||||||
|
}}
|
||||||
|
class:border-2={currentTheme === name}
|
||||||
|
class:border-primary={currentTheme === name}
|
||||||
|
class="btn btn-ghost w-full hover:bg-primary group rounded-lg flex bg-base-100 p-2 transition-all">
|
||||||
|
<p class="flex-1 text-left text-base-content group-hover:text-primary-content transition-color">
|
||||||
|
{text ?? name}
|
||||||
|
</p>
|
||||||
|
<div class="flex-none m-auto flex gap-1">
|
||||||
|
<div class="bg-primary w-2 h-4 rounded" />
|
||||||
|
<div class="bg-secondary w-2 h-4 rounded" />
|
||||||
|
<div class="bg-accent w-2 h-4 rounded" />
|
||||||
|
<div class="bg-neutral w-2 h-4 rounded" />
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<ul
|
|
||||||
tabindex="0"
|
|
||||||
class="flex shadow-2xl menu dropdown-content bg-base-100 text-base-content rounded-box w-52 p-2 gap-2 overflow-y-auto max-h-[21.5rem]"
|
|
||||||
class:hidden={!pin}>
|
|
||||||
{#each theme as { name, text }}
|
|
||||||
<button
|
|
||||||
data-theme={name}
|
|
||||||
on:click={() => {
|
|
||||||
currentTheme = name
|
|
||||||
localStorage.setItem('theme', name)
|
|
||||||
}}
|
|
||||||
class:border-2={currentTheme === name}
|
|
||||||
class:border-primary={currentTheme === name}
|
|
||||||
class="btn btn-ghost hover:bg-primary group rounded-lg flex bg-base-100 p-2 transition-all">
|
|
||||||
<p class="flex-1 text-left text-base-content group-hover:text-primary-content transition-color">
|
|
||||||
{text ?? name}
|
|
||||||
</p>
|
|
||||||
<div class="flex-none m-auto flex gap-1">
|
|
||||||
<div class="bg-primary w-2 h-4 rounded" />
|
|
||||||
<div class="bg-secondary w-2 h-4 rounded" />
|
|
||||||
<div class="bg-accent w-2 h-4 rounded" />
|
|
||||||
<div class="bg-neutral w-2 h-4 rounded" />
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{:else}
|
||||||
|
<div in:fly={{ x: 50, duration: 300, delay: 300 }} out:fly={{ x: 50, duration: 300 }} class="navbar">
|
||||||
|
<Search />
|
||||||
|
<button on:click={() => (search = !search)} tabindex="0" class="btn btn-square btn-ghost">
|
||||||
|
<span class="i-heroicons-outline-x" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
export let pin: boolean
|
export let pin: boolean
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||||
|
<!-- reference: https://github.com/saadeghi/daisyui/issues/1285 -->
|
||||||
<div class="dropdown lg:hidden">
|
<div class="dropdown lg:hidden">
|
||||||
<label for="navbar-dropdown" tabindex="0" class="btn btn-square btn-ghost">
|
<label for="navbar-dropdown" tabindex="0" class="btn btn-square btn-ghost">
|
||||||
<span class="i-heroicons-outline-menu-alt-1" />
|
<span class="i-heroicons-outline-menu-alt-1" />
|
||||||
|
@ -14,11 +16,12 @@
|
||||||
id="navbar-dropdown"
|
id="navbar-dropdown"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
class:hidden={!pin}
|
class:hidden={!pin}
|
||||||
class="menu menu-compact dropdown-content bg-base-100 text-base-content shadow-lg rounded-box max-w-52 p-2">
|
class="menu menu-compact dropdown-content bg-base-100 text-base-content shadow-lg rounded-box min-w-max max-w-52 p-2
|
||||||
|
">
|
||||||
{#each nav as { text, link, children }}
|
{#each nav as { text, link, children }}
|
||||||
{#if link && !children}
|
{#if link && !children}
|
||||||
<li>
|
<li>
|
||||||
<a sveltekit:prefetch class:font-bold={link === path} href={link}>{text}</a>
|
<a class:font-bold={link === path} href={link}>{text}</a>
|
||||||
</li>
|
</li>
|
||||||
{:else if children}
|
{:else if children}
|
||||||
<li tabindex="0">
|
<li tabindex="0">
|
||||||
|
@ -29,7 +32,7 @@
|
||||||
<ul class="bg-base-100 text-base-content shadow-lg p-2">
|
<ul class="bg-base-100 text-base-content shadow-lg p-2">
|
||||||
{#each children as { text, link }}
|
{#each children as { text, link }}
|
||||||
<li>
|
<li>
|
||||||
<a sveltekit:prefetch class:font-bold={link === path} href={link}>{text}</a>
|
<a class:font-bold={link === path} href={link}>{text}</a>
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -49,18 +52,19 @@
|
||||||
{#each nav as { text, link, children }}
|
{#each nav as { text, link, children }}
|
||||||
{#if link && !children}
|
{#if link && !children}
|
||||||
<li>
|
<li>
|
||||||
<a sveltekit:prefetch class:font-bold={link === path} href={link}>{text}</a>
|
<a class="!rounded-btn" class:font-bold={link === path} href={link}>{text}</a>
|
||||||
</li>
|
</li>
|
||||||
{:else if children}
|
{:else if children}
|
||||||
<li tabindex="0">
|
<li>
|
||||||
<span class:font-bold={children.some(({ link }) => link === path)} class="gap-1">
|
<span class:font-bold={children.some(({ link }) => link === path)} class="!rounded-btn gap-1">
|
||||||
{text}
|
{text}
|
||||||
<span class="i-heroicons-solid-chevron-down -mr-1" />
|
<span class="i-heroicons-solid-chevron-down -mr-1" />
|
||||||
</span>
|
</span>
|
||||||
<ul class="bg-base-100 text-base-content shadow-lg p-2">
|
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||||
|
<ul tabindex="0" class="menu rounded-box bg-base-100 text-base-content shadow-lg p-2">
|
||||||
{#each children as { text, link }}
|
{#each children as { text, link }}
|
||||||
<li>
|
<li>
|
||||||
<a sveltekit:prefetch class:font-bold={link === path} href={link}>{text}</a>
|
<a class:font-bold={link === path} href={link}>{text}</a>
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -1,102 +1,21 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount, createEventDispatcher } from 'svelte'
|
|
||||||
import { site } from '$lib/config/site'
|
import { site } from '$lib/config/site'
|
||||||
import { header as headerConfig } from '$lib/config/general'
|
import { header as headerConfig } from '$lib/config/general'
|
||||||
import Typeahead from 'svelte-typeahead'
|
|
||||||
import { page } from '$app/stores'
|
|
||||||
// ref : https://github.com/saadeghi/daisyui/blob/master/src/docs/src/components/Search.svelte
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<form>
|
<form
|
||||||
<input type="checkbox" id="search-modal" class="modal-toggle " />
|
action={headerConfig?.search?.provider === 'duckduckgo' ? '//duckduckgo.com/' : '//google.com/search'}
|
||||||
<label for="search-modal" class="modal cursor-pointer w-full items-start pt-16 ">
|
method="get"
|
||||||
<label class="modal-box w-11/12 max-w-4xl " for="">
|
class="flex-1">
|
||||||
<div class="form-control">
|
<input
|
||||||
<span
|
type="text"
|
||||||
class="i-heroicons-outline-search text-base-content pointer-events-none absolute z-10 my-2 ml-3 stroke-current opacity-60 w-5" />
|
name="q"
|
||||||
</div>
|
class="input input-ghost input-bordered xl:bg-base-100 xl:text-base-content transition-all w-full h-12" />
|
||||||
<Typeahead placeholder="Search is not avaible …" limit={8} label="Search" inputAfterSelect="clear">
|
<input
|
||||||
<div class="py-1 text-sm" />
|
type="hidden"
|
||||||
</Typeahead>
|
name={headerConfig?.search?.provider === 'duckduckgo' ? 'sites' : 'sitesearch'}
|
||||||
<div class="pointer-events-none absolute right-14 top-8 gap-1 opacity-50 hidden lg:flex">
|
value={site.protocol + site.domain} />
|
||||||
<kbd class="kbd kbd-sm">ESC</kbd>
|
<button type="submit" class="btn btn-square btn-ghost ml-2">
|
||||||
</div>
|
<span class="i-heroicons-outline-search" />
|
||||||
</label>
|
</button>
|
||||||
</label>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!-- <script lang="ts">
|
|
||||||
import { onMount, createEventDispatcher } from "svelte"
|
|
||||||
import { page } from "$app/stores"
|
|
||||||
import { goto } from "$app/navigation"
|
|
||||||
import Typeahead from "svelte-typeahead"
|
|
||||||
import { pages } from "@src/lib/data.js"
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
let searchIndex = []
|
|
||||||
pages.forEach((group) => {
|
|
||||||
group.items.forEach((item) => {
|
|
||||||
searchIndex.push(item)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
let seachboxEl
|
|
||||||
function handleKeydown(e) {
|
|
||||||
if ((e.keyCode === 75 && e.metaKey) || (e.keyCode === 75 && e.ctrlKey)) {
|
|
||||||
e.preventDefault()
|
|
||||||
seachboxEl.querySelector("input[type=search]").focus()
|
|
||||||
dispatch("focus")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onSelect({ detail }) {
|
|
||||||
goto(searchIndex[detail.originalIndex].href)
|
|
||||||
dispatch("search", detail)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svelte:window on:keydown={handleKeydown} />
|
|
||||||
-->
|
|
||||||
<style global>
|
|
||||||
.searchbox [data-svelte-typeahead] {
|
|
||||||
background: none;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
[data-svelte-search] label {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
[data-svelte-search] input {
|
|
||||||
background-color: transparent;
|
|
||||||
color: inherit;
|
|
||||||
border-radius: 0.5em;
|
|
||||||
padding-left: 2em;
|
|
||||||
}
|
|
||||||
[data-svelte-search] input::placeholder {
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
[data-svelte-search] input:focus {
|
|
||||||
outline-color: hsla(var(--bc) / 0.2);
|
|
||||||
background-color: hsl(var(--b1));
|
|
||||||
color: hsla(var(--bc));
|
|
||||||
}
|
|
||||||
[data-svelte-typeahead] .svelte-typeahead-list {
|
|
||||||
transform: translateY(0.5em);
|
|
||||||
background: hsl(var(--b1));
|
|
||||||
border-radius: var(--rounded-btn);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
[data-svelte-typeahead] .svelte-typeahead-list .selected,
|
|
||||||
[data-svelte-typeahead] .svelte-typeahead-list .selected:hover {
|
|
||||||
background: hsl(var(--n));
|
|
||||||
color: hsl(var(--nc));
|
|
||||||
}
|
|
||||||
[data-svelte-typeahead] .svelte-typeahead-list li {
|
|
||||||
color: hsl(var(--bc));
|
|
||||||
}
|
|
||||||
[data-svelte-typeahead] .svelte-typeahead-list li:hover {
|
|
||||||
background: hsl(var(--b2));
|
|
||||||
color: hsl(var(--bc));
|
|
||||||
}
|
|
||||||
[data-svelte-typeahead] .svelte-typeahead-list li:not(:last-of-type) {
|
|
||||||
border-bottom-color: hsla(var(--bc) / 0.1);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let post: Urara.Post
|
export let post: Urara.Post
|
||||||
const actions = import.meta.glob<{ default: unknown }>('/src/lib/components/actions/*.svelte', { eager: true })
|
const actions = import.meta.glob<any>('/src/lib/components/actions/*.svelte', { eager: true, import: 'default' })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="sticky top-24 hidden xl:flex flex-col gap-4 w-fit h-[calc(100vh-12rem)] ml-auto mr-8 my-8 justify-center">
|
<div class="sticky top-24 hidden xl:flex flex-col gap-4 w-fit max-h-[calc(100vh-12rem)] ml-auto mr-6 my-8 justify-center">
|
||||||
{#if Object.keys(actions).length}
|
{#if Object.keys(actions).length}
|
||||||
{#each Object.values(actions) as action}
|
{#each Object.values(actions) as action}
|
||||||
<svelte:component this={action.default} {post} />
|
<svelte:component this={action} {post} />
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { browser } from '$app/env'
|
import { browser } from '$app/environment'
|
||||||
import { post as postConfig } from '$lib/config/post'
|
import { post as postConfig } from '$lib/config/post'
|
||||||
import { posts as storedPosts } from '$lib/stores/posts'
|
import { posts as storedPosts } from '$lib/stores/posts'
|
||||||
import { title as storedTitle } from '$lib/stores/title'
|
import { title as storedTitle } from '$lib/stores/title'
|
||||||
|
@ -35,11 +35,10 @@
|
||||||
itemprop="blogPost"
|
itemprop="blogPost"
|
||||||
class:md:mb-8={!preview}
|
class:md:mb-8={!preview}
|
||||||
class:lg:mb-16={!preview}
|
class:lg:mb-16={!preview}
|
||||||
class:h-entry={preview}
|
|
||||||
class:group={preview}
|
class:group={preview}
|
||||||
class:image-full={preview && post.type === 'article' && post.image}
|
class:image-full={preview && post.type === 'article' && post.image}
|
||||||
class:before:!rounded-none={preview && post.image}
|
class:before:!rounded-none={preview && post.image}
|
||||||
class="card bg-base-100 rounded-none md:rounded-box md:shadow-xl z-10">
|
class="h-entry card bg-base-100 rounded-none md:rounded-box md:shadow-lg md:shadow-grey-10 overflow-hidden z-10">
|
||||||
{#if !preview && postConfig.bridgy}
|
{#if !preview && postConfig.bridgy}
|
||||||
<div id="bridgy" class="hidden">
|
<div id="bridgy" class="hidden">
|
||||||
{#each post.flags?.some( flag => flag.startsWith('bridgy') ) ? post.flags.flatMap( flag => (flag.startsWith('bridgy') ? flag.slice(7) : []) ) : [...(postConfig.bridgy.post ?? []), ...(postConfig.bridgy[post.type] ?? [])] as target}
|
{#each post.flags?.some( flag => flag.startsWith('bridgy') ) ? post.flags.flatMap( flag => (flag.startsWith('bridgy') ? flag.slice(7) : []) ) : [...(postConfig.bridgy.post ?? []), ...(postConfig.bridgy[post.type] ?? [])] as target}
|
||||||
|
@ -73,9 +72,10 @@
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
{#if post.image && !preview}
|
{#if post.image && !preview}
|
||||||
<figure
|
<figure
|
||||||
class={`md:order-last mb-4 ${post.type === 'article' ? 'flex-col gap-2 -mx-4 -mt-8 md:mt-0' : 'flex-col -mx-8'}`}>
|
class={`md:order-last rounded-lg mb-4 ${post.type === 'article' ? 'flex-col gap-2 -mt-8 md:mt-0' : 'flex-col -mx-8'}'
|
||||||
|
}`}>
|
||||||
<Image
|
<Image
|
||||||
class={`${post.type === 'article' ? 'u-featured rounded-box shadow-xl' : 'u-photo'}`}
|
class={`${post.type === 'article' ? 'u-featured' : 'u-photo'}`}
|
||||||
src={post.image}
|
src={post.image}
|
||||||
alt={post.image}
|
alt={post.image}
|
||||||
{loading}
|
{loading}
|
||||||
|
@ -91,7 +91,7 @@
|
||||||
<a itemprop="url" class="u-url p-name" href={post.path}>{post.title ?? post.path.slice(1)}</a>
|
<a itemprop="url" class="u-url p-name" href={post.path}>{post.title ?? post.path.slice(1)}</a>
|
||||||
</h2>
|
</h2>
|
||||||
{:else}
|
{:else}
|
||||||
<h1 itemprop="name headline" class="card-title text-3xl mb-8 p-name">{post.title ?? post.path.slice(1)}</h1>
|
<h1 itemprop="name headline" class="card-title text-2xl mb-8 p-name">{post.title ?? post.path.slice(1)}</h1>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{#if post.summary}
|
{#if post.summary}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import { toSnake } from '$lib/utils/case'
|
import { toSnake } from '$lib/utils/case'
|
||||||
export let post: Urara.Post
|
export let post: Urara.Post
|
||||||
export let config: CommentConfig
|
export let config: CommentConfig
|
||||||
const comments = import.meta.glob<{ default: unknown }>('/src/lib/components/comments/*.svelte', { eager: true })
|
const comments = import.meta.glob<any>('/src/lib/components/comments/*.svelte', { eager: true, import: 'default' })
|
||||||
let currentComment: string | undefined = undefined
|
let currentComment: string | undefined = undefined
|
||||||
let currentConfig: unknown | undefined = undefined
|
let currentConfig: unknown | undefined = undefined
|
||||||
currentComment = localStorage.getItem('comment') ?? toSnake(config.use[0])
|
currentComment = localStorage.getItem('comment') ?? toSnake(config.use[0])
|
||||||
|
@ -14,8 +14,9 @@
|
||||||
{#if config?.use.length > 0}
|
{#if config?.use.length > 0}
|
||||||
<div id="post-comment" class="card card-body">
|
<div id="post-comment" class="card card-body">
|
||||||
{#if config.use.length > 1}
|
{#if config.use.length > 1}
|
||||||
<div class="tabs w-full mb-8" class:tabs-boxed={config?.['style'] === 'boxed'}>
|
<div class="tabs w-full" class:tabs-boxed={config?.['style'] === 'boxed'}>
|
||||||
{#each config.use as name}
|
{#each config.use as name}
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<span
|
<span
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
currentComment = toSnake(name)
|
currentComment = toSnake(name)
|
||||||
|
@ -33,7 +34,7 @@
|
||||||
{#if currentComment}
|
{#if currentComment}
|
||||||
{#key currentComment}
|
{#key currentComment}
|
||||||
<svelte:component
|
<svelte:component
|
||||||
this={comments[`/src/lib/components/comments/${currentComment}.svelte`].default}
|
this={comments[`/src/lib/components/comments/${currentComment}.svelte`]}
|
||||||
{post}
|
{post}
|
||||||
config={currentConfig} />
|
config={currentConfig} />
|
||||||
{/key}
|
{/key}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { fly } from 'svelte/transition'
|
import { fly } from 'svelte/transition'
|
||||||
import { browser } from '$app/env'
|
import { browser } from '$app/environment'
|
||||||
import Card from '$lib/components/post_card.svelte'
|
import Card from '$lib/components/post_card.svelte'
|
||||||
import Head from '$lib/components/head.svelte'
|
import Head from '$lib/components/head.svelte'
|
||||||
import Toc from '$lib/components/post_toc.svelte'
|
import Toc from '$lib/components/post_toc.svelte'
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
<div
|
<div
|
||||||
in:fly={{ x: 25, duration: 300, delay: 500 }}
|
in:fly={{ x: 25, duration: 300, delay: 500 }}
|
||||||
out:fly={{ x: 25, duration: 300 }}
|
out:fly={{ x: 25, duration: 300 }}
|
||||||
class="flex-1 w-full max-w-screen-md order-first ease-out transform mx-auto xl:mr-0">
|
class="flex-1 w-full order-first ease-out transform mx-auto xl:mr-0 xl:ml-0">
|
||||||
{#if browser}
|
{#if browser}
|
||||||
<Action {post} />
|
<Action {post} />
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
<div
|
<div
|
||||||
in:fly={{ x: -25, duration: 300, delay: 500 }}
|
in:fly={{ x: -25, duration: 300, delay: 500 }}
|
||||||
out:fly={{ x: -25, duration: 300 }}
|
out:fly={{ x: -25, duration: 300 }}
|
||||||
class="flex-1 w-full max-w-screen-md xl:order-last ease-out transform mx-auto xl:mr-0">
|
class="flex-1 w-full xl:order-last ease-out transform mx-auto xl:ml-0 xl:mr-0">
|
||||||
{#if browser && post.toc}
|
{#if browser && post.toc}
|
||||||
<div class="h-full hidden xl:block">
|
<div class="h-full hidden xl:block">
|
||||||
<Toc toc={post.toc} />
|
<Toc toc={post.toc} />
|
||||||
|
|
|
@ -7,10 +7,9 @@
|
||||||
<nav class="flex flex-col md:flex-row flex-warp justify-evenly">
|
<nav class="flex flex-col md:flex-row flex-warp justify-evenly">
|
||||||
{#if prev}
|
{#if prev}
|
||||||
<div
|
<div
|
||||||
href={prev.path}
|
|
||||||
class:image-full={prev['image']}
|
class:image-full={prev['image']}
|
||||||
class:md:rounded-r-box={next && !next['image']}
|
class:md:rounded-r-box={next && !next['image']}
|
||||||
class="flex-1 card group rounded-none before:!rounded-none">
|
class="flex-1 card group rounded-none before:!rounded-none overflow-hidden">
|
||||||
{#if prev['image']}
|
{#if prev['image']}
|
||||||
<figure class="!block">
|
<figure class="!block">
|
||||||
<Image
|
<Image
|
||||||
|
@ -34,10 +33,9 @@
|
||||||
{/if}
|
{/if}
|
||||||
{#if next}
|
{#if next}
|
||||||
<div
|
<div
|
||||||
href={next.path}
|
|
||||||
class:image-full={next['image']}
|
class:image-full={next['image']}
|
||||||
class:md:rounded-l-box={prev && !prev['image']}
|
class:md:rounded-l-box={prev && !prev['image']}
|
||||||
class="flex-1 card group rounded-none before:!rounded-none">
|
class="flex-1 card group rounded-none before:!rounded-none overflow-hidden">
|
||||||
{#if next['image']}
|
{#if next['image']}
|
||||||
<figure class="!block">
|
<figure class="!block">
|
||||||
<Image
|
<Image
|
||||||
|
|
|
@ -6,25 +6,12 @@
|
||||||
|
|
||||||
<div class="flex flex-wrap gap-2 rounded-box outline outline-neutral/10 p-4 {className}">
|
<div class="flex flex-wrap gap-2 rounded-box outline outline-neutral/10 p-4 {className}">
|
||||||
<span class="flex-none font-bold uppercase opacity-30">Reply to: </span>
|
<span class="flex-none font-bold uppercase opacity-30">Reply to: </span>
|
||||||
{#if Array.isArray(in_reply_to)}
|
<a
|
||||||
{#each in_reply_to as reply}
|
href={in_reply_to}
|
||||||
<a
|
rel="noopener noreferrer external"
|
||||||
href={reply}
|
target="_blank"
|
||||||
rel="noopener external"
|
class="ml-auto flex-none flex rounded-badge bg-base-200 hover:bg-base-300 transition-all gap-2 px-4 u-in-reply-to">
|
||||||
target="_blank"
|
<span class="i-heroicons-outline-reply my-auto !w-4 !h-4" />
|
||||||
class="flex-none flex rounded-badge bg-base-200 hover:bg-base-300 transition-all gap-2 px-4 u-in-reply-to">
|
{in_reply_to}
|
||||||
<span class="i-heroicons-outline-reply my-auto !w-4 !h-4" />
|
</a>
|
||||||
{reply}
|
|
||||||
</a>
|
|
||||||
{/each}
|
|
||||||
{:else}
|
|
||||||
<a
|
|
||||||
href={in_reply_to}
|
|
||||||
rel="noopener external"
|
|
||||||
target="_blank"
|
|
||||||
class="ml-auto flex-none flex rounded-badge bg-base-200 hover:bg-base-300 transition-all gap-2 px-4 u-in-reply-to">
|
|
||||||
<span class="i-heroicons-outline-reply my-auto !w-4 !h-4" />
|
|
||||||
{in_reply_to}
|
|
||||||
</a>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,15 +18,15 @@
|
||||||
{site.author.name}
|
{site.author.name}
|
||||||
</a>
|
</a>
|
||||||
<span class:hidden={preview} class="opacity-50">/</span>
|
<span class:hidden={preview} class="opacity-50">/</span>
|
||||||
<a href={post.path} class="swap hover:swap-active u-url u-uid">
|
<a href={post.path} class="u-url u-uid swap group/time">
|
||||||
<time
|
<time
|
||||||
class="swap-off font-semibold opacity-75 duration-500 ease-in-out mr-auto dt-published"
|
class="group-hover/time:opacity-0 font-semibold opacity-75 duration-500 ease-in-out mr-auto dt-published"
|
||||||
datetime={jsonPublished}
|
datetime={jsonPublished}
|
||||||
itemprop="datePublished">
|
itemprop="datePublished">
|
||||||
{stringPublished}
|
{stringPublished}
|
||||||
</time>
|
</time>
|
||||||
<time
|
<time
|
||||||
class="swap-on font-semibold text-primary duration-500 ease-in-out mr-auto dt-updated"
|
class="opacity-0 group-hover/time:opacity-100 font-semibold text-primary duration-500 ease-in-out mr-auto dt-updated"
|
||||||
datetime={jsonUpdated}
|
datetime={jsonUpdated}
|
||||||
itemprop="dateModified">
|
itemprop="dateModified">
|
||||||
{stringUpdated}
|
{stringUpdated}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount, onDestroy } from 'svelte'
|
import { onMount, onDestroy } from 'svelte'
|
||||||
import Tree from '$lib/components/post_toc_tree.svelte'
|
|
||||||
export let toc: Urara.Post.Toc[]
|
export let toc: Urara.Post.Toc[]
|
||||||
|
|
||||||
let intersecting: string[] = []
|
let intersecting: string[] = []
|
||||||
|
@ -34,7 +33,7 @@
|
||||||
// @ts-ignore: Cannot find name 'headingsObserver'
|
// @ts-ignore: Cannot find name 'headingsObserver'
|
||||||
if (typeof headingsObserver !== 'undefined') headingsObserver.disconnect()
|
if (typeof headingsObserver !== 'undefined') headingsObserver.disconnect()
|
||||||
// @ts-ignore: Cannot find name 'articleObserver'
|
// @ts-ignore: Cannot find name 'articleObserver'
|
||||||
if (typeof headingsObserver !== 'undefined') articleObserver.disconnect()
|
if (typeof articleObserver !== 'undefined') articleObserver.disconnect()
|
||||||
})
|
})
|
||||||
|
|
||||||
$: if (intersecting.length > 0) bordered = intersecting
|
$: if (intersecting.length > 0) bordered = intersecting
|
||||||
|
@ -53,16 +52,29 @@
|
||||||
aria-label="TableOfContent"
|
aria-label="TableOfContent"
|
||||||
dir="rtl"
|
dir="rtl"
|
||||||
class="max-h-[calc(100vh-12rem)] overflow-y-hidden hover:overflow-y-auto">
|
class="max-h-[calc(100vh-12rem)] overflow-y-hidden hover:overflow-y-auto">
|
||||||
<Tree
|
<ul dir="ltr" id="toc-list-root">
|
||||||
toc={toc.reduce(
|
{#each toc as { depth, title, slug }}
|
||||||
(acc, heading) => {
|
<li id={`toc-item-${slug}`} class="flex flex-col">
|
||||||
let parent = acc
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
// @ts-ignore Type 'Toc | undefined' is not assignable to type 'Toc.' ts(2322)
|
<span
|
||||||
while (parent.depth + 1 < heading.depth) parent = parent.children.at(-1)
|
dir="ltr"
|
||||||
parent.children = [...(parent.children ?? []), { ...heading, children: [] }]
|
on:click={() =>
|
||||||
return acc
|
// @ts-ignore Object is possibly 'null'. ts(2531)
|
||||||
},
|
document.getElementById(slug).scrollIntoView({ behavior: 'smooth' })}
|
||||||
{ depth: toc[0].depth - 1, children: [] }
|
id={`toc-link-${slug}`}
|
||||||
)} />
|
class="cursor-pointer border-l-4 border-transparent transition-all hover:border-primary hover:bg-base-content hover:bg-opacity-10 active:bg-primary active:text-primary-content active:font-bold pr-4 {depth <=
|
||||||
|
2
|
||||||
|
? 'py-3'
|
||||||
|
: 'py-2'}"
|
||||||
|
class:pl-4={depth <= 2}
|
||||||
|
class:pl-8={depth === 3}
|
||||||
|
class:pl-12={depth === 4}
|
||||||
|
class:pl-16={depth === 5}
|
||||||
|
class:pl-20={depth === 6}>
|
||||||
|
{title}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
export let toc: Urara.Post.Toc
|
|
||||||
const { title, slug, depth, children } = toc
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if title}
|
|
||||||
<span
|
|
||||||
dir="ltr"
|
|
||||||
on:click={() =>
|
|
||||||
// @ts-ignore Object is possibly 'null'. ts(2531)
|
|
||||||
document.getElementById(slug).scrollIntoView({ behavior: 'smooth' })}
|
|
||||||
id={`toc-link-${slug}`}
|
|
||||||
class="cursor-pointer border-l-4 border-transparent transition-all hover:border-primary hover:bg-base-content hover:bg-opacity-10 active:bg-primary active:text-primary-content active:font-bold pr-4 {depth <=
|
|
||||||
2
|
|
||||||
? 'py-3'
|
|
||||||
: 'py-2'}"
|
|
||||||
class:pl-4={depth <= 2}
|
|
||||||
class:pl-8={depth === 3}
|
|
||||||
class:pl-12={depth === 4}
|
|
||||||
class:pl-16={depth === 5}
|
|
||||||
class:pl-20={depth === 6}>
|
|
||||||
{title}
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
{#if children}
|
|
||||||
<ul dir="ltr" id={`toc-list-${slug ?? 'root'}`}>
|
|
||||||
{#each children as child}
|
|
||||||
<li id={`toc-item-${child.slug}`} class="flex flex-col">
|
|
||||||
<svelte:self toc={child} />
|
|
||||||
</li>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
{/if}
|
|
|
@ -1,10 +1,37 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
/* @see {@link https://github.com/sveltejs/kit/issues/241#issuecomment-1363621896} */
|
||||||
|
|
||||||
|
type Image = {
|
||||||
|
src: string
|
||||||
|
w: number
|
||||||
|
h: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const sources = import.meta.glob<Image[]>(['/src/static/**/*.{jpg,jpeg,png,webp,avif}', '!/src/static/assets'], {
|
||||||
|
query: {
|
||||||
|
format: 'avif',
|
||||||
|
quality: '80',
|
||||||
|
width: '736',
|
||||||
|
source: ''
|
||||||
|
},
|
||||||
|
import: 'default',
|
||||||
|
eager: true
|
||||||
|
})
|
||||||
|
|
||||||
let className: string | undefined = undefined
|
let className: string | undefined = undefined
|
||||||
export { className as class }
|
export { className as class }
|
||||||
export let src: string
|
export let src: string
|
||||||
export let alt: string = src
|
export let alt: string = src
|
||||||
export let loading: 'eager' | 'lazy' = 'lazy'
|
export let loading: 'eager' | 'lazy' = 'lazy'
|
||||||
export let decoding: 'async' | 'sync' | 'auto' = 'async'
|
export let decoding: 'async' | 'sync' | 'auto' = 'async'
|
||||||
|
let source: Image[] | undefined = sources[`/src/static${src}`]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<img {src} {alt} class={className ?? 'rounded-lg my-2'} {loading} {decoding} />
|
{#if source}
|
||||||
|
<picture>
|
||||||
|
<source srcset={source.map(({ src, w }) => `${src} ${w}w`).join(', ')} type="image/avif" />
|
||||||
|
<img {src} {alt} class={className ?? 'rounded-lg my-2'} {loading} {decoding} />
|
||||||
|
</picture>
|
||||||
|
{:else}
|
||||||
|
<img {src} {alt} class={className ?? 'rounded-lg my-2'} {loading} {decoding} />
|
||||||
|
{/if}
|
||||||
|
|
13
src/lib/components/transition.svelte
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { fly } from 'svelte/transition'
|
||||||
|
export let path: string = ''
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#key path}
|
||||||
|
<div
|
||||||
|
class="bg-base-100 md:bg-base-200 min-h-screen pt-16 md:pb-8 lg:pb-16"
|
||||||
|
in:fly={{ y: 100, duration: 300, delay: 300 }}
|
||||||
|
out:fly={{ y: -100, duration: 300 }}>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
{/key}
|
|
@ -52,19 +52,11 @@ export const friends: Friend[] = [
|
||||||
title: '野生栗子🌰',
|
title: '野生栗子🌰',
|
||||||
link: 'https://blog.chestnut.monster/'
|
link: 'https://blog.chestnut.monster/'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'summerblue',
|
|
||||||
rel: 'friend',
|
|
||||||
name: '夏诤',
|
|
||||||
title: 'SummberBlue',
|
|
||||||
link: 'https://summerblue.space/',
|
|
||||||
descr: '早睡早起身体好'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'loikin',
|
id: 'loikin',
|
||||||
rel: 'friend',
|
rel: 'friend',
|
||||||
name: 'Loikin',
|
name: 'Loikin',
|
||||||
title: '此生未命名',
|
title: '浣心',
|
||||||
link: 'https://blog.loikein.one/',
|
link: 'https://blog.loikein.one/',
|
||||||
descr: '用爱和理性对抗荒谬',
|
descr: '用爱和理性对抗荒谬',
|
||||||
avatar: '/assets/loikin.png'
|
avatar: '/assets/loikin.png'
|
||||||
|
@ -74,8 +66,42 @@ export const friends: Friend[] = [
|
||||||
rel: 'friend',
|
rel: 'friend',
|
||||||
name: '鲨',
|
name: '鲨',
|
||||||
title: '一只脆脆鲨',
|
title: '一只脆脆鲨',
|
||||||
link: 'http://blog.sharktale.xyz/',
|
link: 'https://woods.sharktale.xyz/',
|
||||||
descr: '遇见一只脆脆鲨',
|
descr: '遇见一只脆脆鲨',
|
||||||
avatar: 'https://s2.loli.net/2022/03/30/xwOzn9G8TIqFPvR.jpg'
|
avatar: 'https://s2.loli.net/2022/03/30/xwOzn9G8TIqFPvR.jpg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'kwaa',
|
||||||
|
rel: 'friend',
|
||||||
|
name: '藍 +85CD',
|
||||||
|
title: './kwaa.dev',
|
||||||
|
link: 'https://kwaa.dev',
|
||||||
|
descr: '[DATA EXPUNGED]',
|
||||||
|
avatar: 'https://kwaa.dev/assets/any@512.webp'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'debula',
|
||||||
|
rel: 'friend',
|
||||||
|
title: '秘密花园',
|
||||||
|
name: '本宝宝',
|
||||||
|
link: 'https://blog.debula.ml',
|
||||||
|
descr: '月亮一直跟着我走,它迷路了吗?',
|
||||||
|
avatar: 'https://blog.debula.ml/usr/uploads/violet.jpg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'zhuzi',
|
||||||
|
rel: 'friend',
|
||||||
|
title: 'Bambooom',
|
||||||
|
name: '竹子',
|
||||||
|
link: 'https://zhuzi.dev',
|
||||||
|
descr: 'Blah Blah Booooom'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'litomore',
|
||||||
|
rel: 'friend',
|
||||||
|
title: "LitoMore's Mind",
|
||||||
|
name: 'LitoMore',
|
||||||
|
link: 'https://litomore.me',
|
||||||
|
avatar: 'https://litomore.me/favicon.svg'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,121 +1,61 @@
|
||||||
import type { ThemeConfig, HeadConfig, HeaderConfig, FooterConfig, DateConfig, FeedConfig } from '$lib/types/general'
|
import type { ThemeConfig, HeadConfig, HeaderConfig, FooterConfig, DateConfig, FeedConfig } from '$lib/types/general'
|
||||||
|
|
||||||
export const theme: ThemeConfig = [
|
export const theme: ThemeConfig = [
|
||||||
{
|
|
||||||
name: 'lemonade',
|
|
||||||
text: 'Light'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'night',
|
|
||||||
text: 'Dark'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'cupcake',
|
|
||||||
text: 'Cupcake'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'bumblebee',
|
|
||||||
text: 'Bumblebee'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'emerald',
|
|
||||||
text: 'Emerald'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'corporate',
|
|
||||||
text: 'Corporate'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'valentine',
|
|
||||||
text: 'Valentine'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'synthwave',
|
|
||||||
text: 'Synthwave'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'retro',
|
|
||||||
text: 'Retro'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'cyberpunk',
|
|
||||||
text: 'Cyberpunk'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'halloween',
|
|
||||||
text: 'Halloween'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'garden',
|
|
||||||
text: 'Garden'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'forest',
|
|
||||||
text: 'Forest'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'aqua',
|
|
||||||
text: 'Aqua'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'lofi',
|
name: 'lofi',
|
||||||
text: 'Lo-Fi'
|
text: 'Light'
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'pastel',
|
|
||||||
text: 'Pastel'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'fantasy',
|
|
||||||
text: 'Fantasy'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'wirefream',
|
|
||||||
text: 'Wireframe'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'black',
|
|
||||||
text: 'Black'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'luxury',
|
|
||||||
text: 'Luxury'
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'dracula',
|
name: 'dracula',
|
||||||
text: 'Dracula'
|
text: 'Dracula'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'cmyk',
|
|
||||||
text: 'CMYK'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'autumn',
|
|
||||||
text: 'Autumn'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'business',
|
|
||||||
text: 'Business'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'acid',
|
|
||||||
text: 'Acid'
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// name: 'lemonade',
|
|
||||||
// text: 'Lemonade'
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// name: 'night',
|
|
||||||
// text: '🌃 Night'
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
name: 'coffee',
|
name: 'coffee',
|
||||||
text: 'Coffee'
|
text: 'Coffee'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'winter',
|
name: 'cupcake',
|
||||||
text: 'Winter'
|
text: 'Cupcake'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'valentine',
|
||||||
|
text: 'Valentine'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'aqua',
|
||||||
|
text: 'Aqua'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'synthwave',
|
||||||
|
text: 'Synthwave'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'night',
|
||||||
|
text: 'Night'
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// name: 'lofi',
|
||||||
|
// text: 'Lo-Fi'
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
name: 'garden',
|
||||||
|
text: 'Garden'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'lemonade',
|
||||||
|
text: 'Lemonade'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cmyk',
|
||||||
|
text: 'CMYK'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'retro',
|
||||||
|
text: 'Retro'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'black',
|
||||||
|
text: 'Black'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -131,7 +71,7 @@ export const head: HeadConfig = {
|
||||||
// Umami Analytics
|
// Umami Analytics
|
||||||
'<script data-cfasync="false" defer data-do-not-track="true" data-website-id="2403ea30-74ff-4ffa-8264-556b9f3b2897" src="https://hexoverc.vercel.app/umami.js"></script>',
|
'<script data-cfasync="false" defer data-do-not-track="true" data-website-id="2403ea30-74ff-4ffa-8264-556b9f3b2897" src="https://hexoverc.vercel.app/umami.js"></script>',
|
||||||
// splitbee
|
// splitbee
|
||||||
'<script async data-cfasync="false" src="https://cdn.splitbee.io/sb.js"></script>',
|
'<script async data-cfasync="false" src="https://cdn.splitbee.io/sb.js"></script>',
|
||||||
// Block Baiduspider
|
// Block Baiduspider
|
||||||
'<meta name="baiduspider" content="noindex,noarchive">',
|
'<meta name="baiduspider" content="noindex,noarchive">',
|
||||||
// Microsub
|
// Microsub
|
||||||
|
@ -160,12 +100,20 @@ export const header: HeaderConfig = {
|
||||||
{
|
{
|
||||||
text: 'About',
|
text: 'About',
|
||||||
link: '/about'
|
link: '/about'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'etc',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
text: 'Bookmarks',
|
||||||
|
link: 'https://airtable.com/shrpftxf6JgRomP2X'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Daily Notes',
|
||||||
|
link: 'https://x.seviche.cc'
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
// ,
|
|
||||||
// {
|
|
||||||
// text: 'Notes',
|
|
||||||
// link: '/notes'
|
|
||||||
// }
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,30 +128,11 @@ export const footer: FooterConfig = {
|
||||||
link: '/privacy'
|
link: '/privacy'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
html: '<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">CC BY-NC-SA 4.0</a>'
|
html: '@Sevi.C All Rights Reserved.',
|
||||||
|
since: '2021'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const date: DateConfig = {
|
export const date: DateConfig = {
|
||||||
// toPublishedString: {
|
|
||||||
// locales: 'en-US',
|
|
||||||
// options: {
|
|
||||||
// year: 'numeric',
|
|
||||||
// weekday: 'short',
|
|
||||||
// month: 'short',
|
|
||||||
// day: 'numeric',
|
|
||||||
// timeZone: 'Asia/Shanghai'
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// toUpdatedString: {
|
|
||||||
// locales: 'en-US',
|
|
||||||
// options: {
|
|
||||||
// year: 'numeric',
|
|
||||||
// weekday: 'short',
|
|
||||||
// month: 'short',
|
|
||||||
// day: 'numeric',
|
|
||||||
// timeZone: 'Asia/Shanghai'
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
locales: 'en-US',
|
locales: 'en-US',
|
||||||
options: {
|
options: {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
|
|
|
@ -5,14 +5,14 @@ export const post: PostConfig = {
|
||||||
post: ['mastodon']
|
post: ['mastodon']
|
||||||
},
|
},
|
||||||
comment: {
|
comment: {
|
||||||
use: ['Webmention', 'Giscus'],
|
use: ['Remark42','Webmention', 'Giscus'],
|
||||||
style: 'boxed',
|
style: 'boxed',
|
||||||
webmention: {
|
webmention: {
|
||||||
username: 'seviche.cc',
|
username: 'sevic.me',
|
||||||
sortBy: 'created',
|
sortBy: 'created',
|
||||||
sortDir: 'down',
|
sortDir: 'down',
|
||||||
form: true,
|
form: true,
|
||||||
commentParade: true
|
commentParade: false
|
||||||
},
|
},
|
||||||
giscus: {
|
giscus: {
|
||||||
// src: 'https://giscus.kwaa.dev/client.js',
|
// src: 'https://giscus.kwaa.dev/client.js',
|
||||||
|
@ -22,16 +22,10 @@ export const post: PostConfig = {
|
||||||
categoryID: 'DIC_kwDOHSra4c4CO9ua',
|
categoryID: 'DIC_kwDOHSra4c4CO9ua',
|
||||||
theme: 'light',
|
theme: 'light',
|
||||||
lang: 'en'
|
lang: 'en'
|
||||||
|
},
|
||||||
|
remark42: {
|
||||||
|
host: 'https://remark42.seviche.cc',
|
||||||
|
no_footer: true
|
||||||
}
|
}
|
||||||
// waline: {
|
|
||||||
// serverURL: 'https://waline-seviche.vercel.app/',
|
|
||||||
// lang: 'en',
|
|
||||||
// emoji: [
|
|
||||||
// 'https://cdn.jsdelivr.net/gh/norevi/waline-blobcatemojis@1.0/blobs',
|
|
||||||
// 'https://cdn.jsdelivr.net/gh/norevi/blob-emoji-for-waline@2.0/blobs-gif',
|
|
||||||
// 'ttps://cdn.jsdelivr.net/gh/norevi/blob-emoji-for-waline@2.0/blobs-png'
|
|
||||||
// ],
|
|
||||||
// requiredMeta: ['nick', 'mail']
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,53 @@ export const projects: Project[] = [
|
||||||
// img: 'https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/06/d4d2489936e4f647c25df6982c6ef924.png',
|
// img: 'https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/06/d4d2489936e4f647c25df6982c6ef924.png',
|
||||||
// link: 'https://haibian.seviche.cc'
|
// link: 'https://haibian.seviche.cc'
|
||||||
// },
|
// },
|
||||||
|
{
|
||||||
|
id: 'Raycast-NeoDB',
|
||||||
|
name: 'Raycast - NeoDB',
|
||||||
|
tags: ['React', 'React Hooks', 'Fediverse', 'TypeScript'],
|
||||||
|
description: 'Search NeoDB items and view item details',
|
||||||
|
feature: 'React',
|
||||||
|
img: 'https://github.com/raycast/extensions/raw/f7cf284a8c14e38aee758adb00120de827a144fb/extensions/neodb//metadata/neodb-1.png',
|
||||||
|
link: 'https://www.raycast.com/SevicheCC/neodb'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Raycast-Mastodon',
|
||||||
|
name: 'Raycast - Mastodon',
|
||||||
|
tags: ['React', 'React Hooks', 'Fediverse', 'TypeScript'],
|
||||||
|
description: 'Publish status from Raycast to Mastodon, and view your bookmarked status',
|
||||||
|
feature: 'React',
|
||||||
|
img: 'https://github.com/raycast/extensions/raw/1824339d4b3b404efb53e4fb09ac79d190437773/extensions/mastodon//media/command.png',
|
||||||
|
link: 'https://www.raycast.com/SevicheCC/mastodon'
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: 'Raycast-miniflux',
|
||||||
|
name: 'Raycast - Miniflux',
|
||||||
|
tags: ['React', 'React Hooks', 'Fediverse', 'TypeScript'],
|
||||||
|
description: 'Search RSS entries from Raycast, and more features',
|
||||||
|
feature: 'React',
|
||||||
|
img: 'https://github.com/raycast/extensions/raw/3fdad375dd06b7f390b629235c6a10c37d05fc79/extensions/miniflux//media/commands.png',
|
||||||
|
link: 'https://www.raycast.com/SevicheCC/miniflux'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Raycast-Akkoma',
|
||||||
|
name: 'Raycast - Akkoma',
|
||||||
|
tags: ['React', 'React Hooks', 'Fediverse', 'TypeScript'],
|
||||||
|
description: 'Publish status from Raycast to Akkoma or Pleroma, and view your bookmarked status',
|
||||||
|
feature: 'React',
|
||||||
|
img: 'https://github.com/raycast/extensions/raw/42e765bc6bf970054fd69abfdc6ab3dd2ea4942d/extensions/akkoma//media/command.png',
|
||||||
|
link: 'https://www.raycast.com/SevicheCC/akkoma'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'miniflux-injector',
|
||||||
|
name: 'Miniflux-injector',
|
||||||
|
tags: ['Svelte', 'Browser Extension', 'Miniflux', 'RSS'],
|
||||||
|
description:
|
||||||
|
"Browser extension for the self-hosted miniflux bookmark service. Fork from <a href='https://github.com/Fivefold/linkding-injector' target='_blank'>linkding-injector</a>",
|
||||||
|
feature: 'Svelte',
|
||||||
|
img: 'https://usc1.contabostorage.com/cc0b816231a841b1b0232d5ef0c6deb1:image/2022/12/7ae29e6a539895491fbb88a28c95aed7.png',
|
||||||
|
link: 'https://github.com/Sevichecc/miniflux-injector'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'fokify',
|
id: 'fokify',
|
||||||
name: 'Fokify ',
|
name: 'Fokify ',
|
||||||
|
|
|
@ -1,36 +1,32 @@
|
||||||
import type { SiteConfig } from '$lib/types/site'
|
import type { SiteConfig } from '$lib/types/site'
|
||||||
|
|
||||||
export const site: SiteConfig = {
|
export const site: SiteConfig = {
|
||||||
protocol: 'https://',
|
protocol: import.meta.env.URARA_SITE_PROTOCOL ?? import.meta.env.DEV ? 'http://' : 'https://',
|
||||||
domain: 'seviche.cc',
|
domain: 'sevic.me',
|
||||||
title: 'Seviche.cc',
|
title: 'sevic.me',
|
||||||
subtitle: 'Tech / Code / Random Life',
|
subtitle: 'Random Frontend-Developer',
|
||||||
lang: 'zh',
|
lang: 'zh',
|
||||||
description: 'Tech / Code / Random Life',
|
description: 'Random Frontend-Developer',
|
||||||
author: {
|
author: {
|
||||||
name: '酸橘汁腌鱼',
|
name: 'Sevi.C',
|
||||||
avatar: '/assets/avatar.jpg',
|
avatar: '/assets/avatar.png',
|
||||||
status: '🖤',
|
status: '🖤',
|
||||||
bio: ' Code / Tech <br> Living a Random Life ',
|
bio: 'Full-stack wizard.',
|
||||||
metadata: [
|
metadata: [
|
||||||
{
|
{
|
||||||
text: '',
|
|
||||||
icon: 'i-mdi-github',
|
icon: 'i-mdi-github',
|
||||||
link: 'https://github.com/sevichecc'
|
link: 'https://github.com/sevichecc'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '',
|
|
||||||
icon: 'i-simple-icons-matrix',
|
icon: 'i-simple-icons-matrix',
|
||||||
link: 'https://matrix.to/#/@seviche:kongwoo.icu'
|
link: 'https://matrix.to/#/@seviche:kongwoo.icu'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '',
|
|
||||||
icon: 'i-heroicons-solid-key',
|
icon: 'i-heroicons-solid-key',
|
||||||
link: 'https://keys.openpgp.org/vks/v1/by-fingerprint/76DF9F9CC0C3619AA12CB914AFF18B986818D8AD',
|
link: '/assets/DDDDDDDD.asc',
|
||||||
rel: 'pgpkey'
|
rel: 'pgpkey'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '',
|
|
||||||
icon: 'i-ic-twotone-rss-feed',
|
icon: 'i-ic-twotone-rss-feed',
|
||||||
link: '/atom.xml',
|
link: '/atom.xml',
|
||||||
rel: 'rss'
|
rel: 'rss'
|
||||||
|
@ -44,6 +40,6 @@ export const site: SiteConfig = {
|
||||||
// }
|
// }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
keywords: ['Tech', 'Code', 'Random Life'],
|
keywords: ['Tech', 'Code', 'Seviche CC', 'Frondend Developer', 'Programming'],
|
||||||
themeColor: '#3D4451'
|
themeColor: '#3D4451'
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,3 @@
|
||||||
import type { WalineEmojiInfo } from '@waline/client'
|
|
||||||
type WalineImageUploader = (image: File) => Promise<string>
|
|
||||||
|
|
||||||
type WalineHighlighter = (code: string, lang: string) => string
|
|
||||||
|
|
||||||
type WalineTexRenderer = (blockMode: boolean, tex: string) => string
|
|
||||||
|
|
||||||
export type PostConfig = {
|
export type PostConfig = {
|
||||||
bridgy?: {
|
bridgy?: {
|
||||||
[kind: string]: ('fed' | 'mastodon' | 'flickr' | 'github' | 'twitter')[]
|
[kind: string]: ('fed' | 'mastodon' | 'flickr' | 'github' | 'twitter')[]
|
||||||
|
@ -22,8 +15,7 @@ export type CommentConfig = {
|
||||||
giscus?: GiscusConfig
|
giscus?: GiscusConfig
|
||||||
/** Utterances config, more at https://utteranc.es */
|
/** Utterances config, more at https://utteranc.es */
|
||||||
utterances?: UtterancesConfig
|
utterances?: UtterancesConfig
|
||||||
/** Waline config, more at https://waline.js.org/en/reference/component.html#texrenderer */
|
remark42?: Remark42Config
|
||||||
waline?: WalineConfig
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WebmentionConfig = {
|
export type WebmentionConfig = {
|
||||||
|
@ -81,39 +73,35 @@ export type UtterancesConfig = {
|
||||||
theme?: string
|
theme?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DisqusConfig = {
|
export type Remark42Config = {
|
||||||
shortname: string
|
/** hostname of Remark42 server, same as REMARK_URL in backend config, e.g. "https://demo.remark42.com" */
|
||||||
lang?: string
|
host: string
|
||||||
}
|
/** the SITE that you passed to Remark42 instance on start of backend. (default: remark) */
|
||||||
|
site_id?: string
|
||||||
// Ref:https://waline.js.org/reference/component.html
|
/** url to the page with comments*/
|
||||||
export type WalineConfig = {
|
url?: string
|
||||||
/** Waline server address url */
|
/** an array of widgets that should be rendered on a page (default: ['embed'] )*/
|
||||||
serverURL: string
|
components?: ['embed' | 'last-comments' | 'counter']
|
||||||
/** Article path id*/
|
/** maximum number of comments that is rendered on mobile version (default: 15 )*/
|
||||||
path?: string
|
max_shown_comments?: number
|
||||||
/** Display language. */
|
/** maximum number of comments in the last comments widget (default: 15 )*/
|
||||||
lang?: string
|
max_last_comments?: number
|
||||||
/** Emoji settings, for details see https://waline.js.org/en/guide/client/emoji.html */
|
/** changes UI theme, (default: light )*/
|
||||||
emoji?: (string | WalineEmojiInfo)[] | false
|
theme?: 'light' | 'dark'
|
||||||
/** Darkmode support */
|
/** title for current comments page (default: document.title)*/
|
||||||
dark?: string | boolean
|
page_title?: string
|
||||||
/** Reviewer attributes. Optional values: 'nick', 'mail', 'link' */
|
/**
|
||||||
meta?: string[]
|
* interface localization,
|
||||||
/** Set required fields*/
|
* English (en), Belarusian (be), Brazilian Portuguese (bp), Bulgarian (bg), Chinese (zh), Finnish (fi), French (fr), German (de), Japanese (ja), Korean (ko), Polish (pl), Russian (ru), Spanish (es), Turkish (tr), Ukrainian (ua), Italian (it) and Vietnamese (vi)
|
||||||
requiredMeta?: string[]
|
* default: en
|
||||||
/** login mode status */
|
*/
|
||||||
login?: string
|
locale?: 'en' | 'be' | 'bp' | 'bg' | 'zh' | 'fi' | 'fr' | 'de' | 'ja' | 'ko' | 'pl' | 'ru' | 'es' | 'tr' | 'ua' | 'it' | 'vi'
|
||||||
/** Comment word s limit. */
|
/** enables email subscription (default: true) */
|
||||||
wordLimit?: number | [number, number]
|
show_email_subscription?: boolean
|
||||||
/**number of comments per page. */
|
/** enables RSS subscription, (default: true) */
|
||||||
pageSize?: number
|
show_rss_subscription?: boolean
|
||||||
/** Custom image upload method. */
|
/** minimized UI with basic info only, (default: false) */
|
||||||
imageUploader?: WalineImageUploader | false
|
simple_view?: boolean
|
||||||
/** Code highlighting, use hanabi by default */
|
/** hides footer with signature and links to Remark42,(default: false) */
|
||||||
highlighter?: WalineHighlighter | false
|
no_footer?: boolean
|
||||||
/** Customize \TeX rendering */
|
|
||||||
texRender?: WalineTexRenderer | false
|
|
||||||
/** Whether show copyright and version in footer. */
|
|
||||||
copyright?: boolean
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import type { FFFAuthor } from 'fff-flavored-frontmatter'
|
import type { FFFAuthor } from 'fff-flavored-frontmatter'
|
||||||
|
|
||||||
export type SiteConfig = {
|
export type SiteConfig = {
|
||||||
/** @deprecated - use `description` instead */
|
|
||||||
descr?: string
|
|
||||||
/** site protocol. for example: `https://` */
|
/** site protocol. for example: `https://` */
|
||||||
protocol: string
|
protocol: string
|
||||||
/** site domain. for example: `example.com` */
|
/** site domain. for example: `example.com` */
|
||||||
|
|
40
src/routes/+layout.svelte
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { LayoutData } from './$types'
|
||||||
|
import { onMount } from 'svelte'
|
||||||
|
import { browser, dev } from '$app/environment'
|
||||||
|
import { genTags } from '$lib/utils/posts'
|
||||||
|
import { posts, tags } from '$lib/stores/posts'
|
||||||
|
import { registerSW } from 'virtual:pwa-register'
|
||||||
|
import Head from '$lib/components/head_static.svelte'
|
||||||
|
import Header from '$lib/components/header.svelte'
|
||||||
|
import Transition from '$lib/components/transition.svelte'
|
||||||
|
import 'uno.css'
|
||||||
|
import '../app.pcss'
|
||||||
|
|
||||||
|
export let data: LayoutData
|
||||||
|
|
||||||
|
let { res, path } = data
|
||||||
|
|
||||||
|
$: if (data) path = data.path
|
||||||
|
|
||||||
|
posts.set(res)
|
||||||
|
tags.set(genTags(res))
|
||||||
|
onMount(
|
||||||
|
() =>
|
||||||
|
!dev &&
|
||||||
|
browser &&
|
||||||
|
registerSW({
|
||||||
|
immediate: true,
|
||||||
|
onRegistered: r => r && setInterval(async () => await r.update(), 198964),
|
||||||
|
onRegisterError: error => console.error(error)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Head />
|
||||||
|
|
||||||
|
<Header {path} />
|
||||||
|
|
||||||
|
<Transition {path}>
|
||||||
|
<slot />
|
||||||
|
</Transition>
|
7
src/routes/+layout.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import type { LayoutLoad } from './$types'
|
||||||
|
export const prerender = true
|
||||||
|
export const trailingSlash = 'always'
|
||||||
|
export const load: LayoutLoad = async ({ url, fetch }) => ({
|
||||||
|
path: url.pathname,
|
||||||
|
res: await fetch('/posts.json').then(res => res.json())
|
||||||
|
})
|
|
@ -2,16 +2,15 @@
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
import { fly } from 'svelte/transition'
|
import { fly } from 'svelte/transition'
|
||||||
import { page } from '$app/stores'
|
import { page } from '$app/stores'
|
||||||
import { browser } from '$app/env'
|
import { browser } from '$app/environment'
|
||||||
|
import { goto } from '$app/navigation'
|
||||||
import { posts as storedPosts, tags as storedTags } from '$lib/stores/posts'
|
import { posts as storedPosts, tags as storedTags } from '$lib/stores/posts'
|
||||||
import { title as storedTitle } from '$lib/stores/title'
|
import { title as storedTitle } from '$lib/stores/title'
|
||||||
import Head from '$lib/components/head.svelte'
|
import Head from '$lib/components/head.svelte'
|
||||||
import Footer from '$lib/components/footer.svelte'
|
import Footer from '$lib/components/footer.svelte'
|
||||||
import Post from '$lib/components/post_card.svelte'
|
import Post from '$lib/components/post_card.svelte'
|
||||||
// import Post from '$lib/components/index_post.svelte'
|
|
||||||
import Profile from '$lib/components/index_profile.svelte'
|
import Profile from '$lib/components/index_profile.svelte'
|
||||||
import RemoteFollow from '$lib/components/extra/follow.svelte'
|
|
||||||
|
|
||||||
let allPosts: Urara.Post[]
|
let allPosts: Urara.Post[]
|
||||||
let allTags: string[]
|
let allTags: string[]
|
||||||
let loaded: boolean
|
let loaded: boolean
|
||||||
|
@ -19,9 +18,7 @@
|
||||||
|
|
||||||
storedTitle.set('')
|
storedTitle.set('')
|
||||||
|
|
||||||
$: storedPosts.subscribe(
|
$: storedPosts.subscribe(storedPosts => (allPosts = storedPosts.filter(post => !post.flags?.includes('unlisted'))))
|
||||||
storedPosts => (allPosts = (storedPosts as Urara.Post[]).filter(post => !post.flags?.includes('unlisted')))
|
|
||||||
)
|
|
||||||
|
|
||||||
$: storedTags.subscribe(storedTags => (allTags = storedTags as string[]))
|
$: storedTags.subscribe(storedTags => (allTags = storedTags as string[]))
|
||||||
|
|
||||||
|
@ -30,7 +27,7 @@
|
||||||
$: if (tags) {
|
$: if (tags) {
|
||||||
posts = !tags ? allPosts : allPosts.filter(post => tags.every(tag => post.tags?.includes(tag)))
|
posts = !tags ? allPosts : allPosts.filter(post => tags.every(tag => post.tags?.includes(tag)))
|
||||||
if (browser && window.location.pathname === '/')
|
if (browser && window.location.pathname === '/')
|
||||||
window.history.replaceState({}, '', tags.length > 0 ? `?tags=${tags.toString()}` : `/`)
|
goto(tags.length > 0 ? `?tags=${tags.toString()}` : `/`, { replaceState: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
@ -125,5 +122,3 @@
|
||||||
{/key}
|
{/key}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<RemoteFollow />
|
|
|
@ -1,38 +0,0 @@
|
||||||
<script lang="ts" context="module">
|
|
||||||
import type { Load } from './__types'
|
|
||||||
export const load: Load = ({ url: { pathname }, error, status }) => ({ props: { pathname, error, status } })
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import Head from '$lib/components/head.svelte'
|
|
||||||
import Footer from '$lib/components/footer.svelte'
|
|
||||||
export let pathname: string
|
|
||||||
export let error: Error
|
|
||||||
export let status: string
|
|
||||||
console.error(status, error.message)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Head page={{ title: status ?? '404', path: pathname ?? '/404' }} />
|
|
||||||
|
|
||||||
<div class="flex flex-col flex-nowrap justify-center xl:flex-row xl:flex-wrap">
|
|
||||||
<div class="flex-none w-full max-w-screen-md mx-auto xl:mx-0">
|
|
||||||
<article
|
|
||||||
itemscope
|
|
||||||
itemtype="https://schema.org/BlogPosting"
|
|
||||||
class="card bg-base-100 rounded-none md:rounded-box shadow-xl md:mb-8 z-10">
|
|
||||||
<main itemprop="articleBody" class="card-body prose urara-prose">
|
|
||||||
<h1 class="opacity-20 text-6xl md:text-[12rem] -mt-2 mb-0">
|
|
||||||
{status ?? '404'}
|
|
||||||
</h1>
|
|
||||||
<h2 class="-mt-12 md:-mt-24">{error.message ?? 'Not found'}</h2>
|
|
||||||
<div class="card-actions">
|
|
||||||
<a href="/" class="btn btn-neutral no-underline shadow-xl hover:shadow-2xl mt-8">
|
|
||||||
<span class="i-heroicons-outline-home -ml-1 mr-2" />
|
|
||||||
Back to Home
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</article>
|
|
||||||
<Footer sticky={true} class="flex-1 md:flex-initial" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,49 +0,0 @@
|
||||||
<script lang="ts" context="module">
|
|
||||||
import type { Load } from './__types'
|
|
||||||
export const prerender = true
|
|
||||||
export const load: Load = async ({ url, fetch }) => ({
|
|
||||||
props: {
|
|
||||||
path: url.pathname,
|
|
||||||
res: await fetch('/posts.json').then(res => res.json())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { onMount } from 'svelte'
|
|
||||||
import { browser, dev } from '$app/env'
|
|
||||||
import { fly } from 'svelte/transition'
|
|
||||||
import { genTags } from '$lib/utils/posts'
|
|
||||||
import { posts, tags } from '$lib/stores/posts'
|
|
||||||
import { registerSW } from 'virtual:pwa-register'
|
|
||||||
import Head from '$lib/components/head_static.svelte'
|
|
||||||
import Header from '$lib/components/header.svelte'
|
|
||||||
import 'uno.css'
|
|
||||||
import '../app.css'
|
|
||||||
export let res: Urara.Post[]
|
|
||||||
export let path: string
|
|
||||||
posts.set(res)
|
|
||||||
tags.set(genTags(res))
|
|
||||||
onMount(
|
|
||||||
() =>
|
|
||||||
!dev &&
|
|
||||||
browser &&
|
|
||||||
registerSW({
|
|
||||||
onRegistered: r => r && setInterval(async () => await r.update(), 198964),
|
|
||||||
onRegisterError: error => console.error(error)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Head />
|
|
||||||
|
|
||||||
<Header {path} />
|
|
||||||
|
|
||||||
{#key path}
|
|
||||||
<div
|
|
||||||
class="bg-base-100 md:bg-base-200 min-h-screen pt-16 md:pb-8 lg:pb-16"
|
|
||||||
in:fly={{ y: 100, duration: 300, delay: 300 }}
|
|
||||||
out:fly={{ y: -100, duration: 300 }}>
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
{/key}
|
|
|
@ -1,49 +1,50 @@
|
||||||
import type { RequestHandler } from '@sveltejs/kit'
|
import type { RequestHandler } from './$types'
|
||||||
import { site } from '$lib/config/site'
|
import { site } from '$lib/config/site'
|
||||||
import { feed } from '$lib/config/general'
|
import { feed } from '$lib/config/general'
|
||||||
import { favicon } from '$lib/config/icon'
|
import { favicon } from '$lib/config/icon'
|
||||||
import { genPosts, genTags } from '$lib/utils/posts'
|
import { genPosts, genTags } from '$lib/utils/posts'
|
||||||
|
|
||||||
const render = async (
|
const render = (posts = genPosts({ postHtml: true, postLimit: feed.limit, filterUnlisted: true })): string =>
|
||||||
posts = genPosts({ postHtml: true, postLimit: feed.limit, filterUnlisted: true })
|
`<?xml version='1.0' encoding='utf-8'?>
|
||||||
): Promise<string> => `<?xml version='1.0' encoding='utf-8'?>
|
|
||||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||||
<id>${site.protocol + site.domain}/</id>
|
<id>${site.protocol + site.domain}/</id>
|
||||||
<title><![CDATA[${site.title}]]></title>${site.subtitle ? `\n <subtitle><![CDATA[${site.subtitle}]]></subtitle>` : ''}${
|
<title><![CDATA[${site.title}]]></title>${site.subtitle ? `\n <subtitle><![CDATA[${site.subtitle}]]></subtitle>` : ''}${
|
||||||
favicon ? `\n <icon>${favicon.src}</icon>` : ''
|
favicon ? `\n <icon>${favicon.src}</icon>` : ''
|
||||||
}
|
}
|
||||||
<link href="${site.protocol + site.domain}" />
|
<link href="${site.protocol + site.domain}" />
|
||||||
<link href="${site.protocol + site.domain}/atom.xml" rel="self" type="application/atom+xml" />${
|
<link href="${site.protocol + site.domain}/atom.xml" rel="self" type="application/atom+xml" />${
|
||||||
feed.hubs?.map(hub => `\n <link href="${hub}" rel="hub"/>`).join('') ?? ''
|
feed.hubs?.map(hub => `\n <link href="${hub}" rel="hub"/>`).join('') ?? ''
|
||||||
}
|
}
|
||||||
<updated>${new Date().toJSON()}</updated>
|
<updated>${new Date().toJSON()}</updated>
|
||||||
<author>
|
<author>
|
||||||
<name><![CDATA[${site.author.name}]]></name>
|
<name><![CDATA[${site.author.name}]]></name>
|
||||||
</author>${genTags(posts)
|
</author>${genTags(posts)
|
||||||
.map(tag => `\n <category term="${tag}" scheme="${site.protocol + site.domain}/?tags=${encodeURI(tag)}" />`)
|
.map(tag => `\n <category term="${tag}" scheme="${site.protocol + site.domain}/?tags=${encodeURI(tag)}" />`)
|
||||||
.join('')}${posts
|
.join('')}${posts
|
||||||
.map(
|
.map(
|
||||||
post => `\n <entry>
|
post => `\n <entry>
|
||||||
<title type="html"><![CDATA[${post.title}]]></title>
|
<title type="html"><![CDATA[${post.title}]]></title>
|
||||||
<link href="${site.protocol + site.domain + post.path}" />
|
<link href="${site.protocol + site.domain + post.path}" />
|
||||||
<id>${site.protocol + site.domain + post.path}</id>
|
<id>${site.protocol + site.domain + post.path}</id>
|
||||||
<published>${new Date(post.published ?? post.created).toJSON()}</published>
|
<published>${new Date(post.published ?? post.created).toJSON()}</published>
|
||||||
<updated>${new Date(post.updated ?? post.published ?? post.created).toJSON()}</updated>${
|
<updated>${new Date(post.updated ?? post.published ?? post.created).toJSON()}</updated>${
|
||||||
post.summary ? `\n <summary type="html"><![CDATA[${post.summary.toString()}]]></summary>` : ''
|
post.summary ? `\n <summary type="html"><![CDATA[${post.summary.toString()}]]></summary>` : ''
|
||||||
}
|
}
|
||||||
<content type="html">
|
<content type="html">
|
||||||
<![CDATA[${post.html}]]>
|
<![CDATA[${post.html}]]>
|
||||||
</content>${post.tags
|
</content>${post.tags
|
||||||
?.map(tag => `\n <category term="${tag}" scheme="${site.protocol + site.domain}/?tags=${encodeURI(tag)}" />`)
|
?.map(tag => `\n <category term="${tag}" scheme="${site.protocol + site.domain}/?tags=${encodeURI(tag)}" />`)
|
||||||
.join('')}
|
.join('')}
|
||||||
</entry>`
|
</entry>`
|
||||||
)
|
)
|
||||||
.join('')}
|
.join('')}
|
||||||
</feed>`
|
</feed>`.trim()
|
||||||
|
|
||||||
export const GET: RequestHandler = async () => ({
|
export const prerender = true
|
||||||
headers: {
|
export const trailingSlash = 'never'
|
||||||
'Content-Type': 'application/atom+xml; charset=utf-8'
|
export const GET: RequestHandler = async () =>
|
||||||
},
|
new Response(render(), {
|
||||||
body: await render()
|
headers: {
|
||||||
})
|
'content-type': 'application/atom+xml; charset=utf-8'
|
||||||
|
}
|
||||||
|
})
|
|
@ -1,10 +1,11 @@
|
||||||
import type { RequestHandler } from '@sveltejs/kit'
|
import type { RequestHandler } from './$types'
|
||||||
|
import { json } from '@sveltejs/kit'
|
||||||
import { site } from '$lib/config/site'
|
import { site } from '$lib/config/site'
|
||||||
import { feed } from '$lib/config/general'
|
import { feed } from '$lib/config/general'
|
||||||
import { favicon, any } from '$lib/config/icon'
|
import { favicon, any } from '$lib/config/icon'
|
||||||
import { genPosts } from '$lib/utils/posts'
|
import { genPosts } from '$lib/utils/posts'
|
||||||
|
|
||||||
const render = async (posts = genPosts({ postHtml: true, postLimit: feed.limit, filterUnlisted: true })) => ({
|
const render = (posts = genPosts({ postHtml: true, postLimit: feed.limit, filterUnlisted: true })) => ({
|
||||||
version: 'https://jsonfeed.org/version/1.1',
|
version: 'https://jsonfeed.org/version/1.1',
|
||||||
title: site.title,
|
title: site.title,
|
||||||
home_page_url: site.protocol + site.domain,
|
home_page_url: site.protocol + site.domain,
|
||||||
|
@ -41,9 +42,11 @@ const render = async (posts = genPosts({ postHtml: true, postLimit: feed.limit,
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
export const GET: RequestHandler = async () => ({
|
export const prerender = true
|
||||||
headers: {
|
export const trailingSlash = 'never'
|
||||||
'Content-Type': 'application/feed+json; charset=utf-8'
|
export const GET: RequestHandler = async () =>
|
||||||
},
|
json(render(), {
|
||||||
body: JSON.stringify(await render(), null, 2)
|
headers: {
|
||||||
})
|
'content-type': 'application/feed+json; charset=utf-8'
|
||||||
|
}
|
||||||
|
})
|
|
@ -1,9 +1,10 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import Masonry from 'svelte-bricks'
|
import type { Friend } from '$lib/config/friends'
|
||||||
import { friends as allFriends } from '$lib/config/friends'
|
import { friends as allFriends } from '$lib/config/friends'
|
||||||
import { title as storedTitle } from '$lib/stores/title'
|
import { title as storedTitle } from '$lib/stores/title'
|
||||||
import Head from '$lib/components/head.svelte'
|
import Head from '$lib/components/head.svelte'
|
||||||
|
import Masonry from 'svelte-bricks'
|
||||||
import FriendComponent from '$lib/components/extra/friend.svelte'
|
import FriendComponent from '$lib/components/extra/friend.svelte'
|
||||||
|
|
||||||
const rnd = Math.random()
|
const rnd = Math.random()
|
||||||
|
@ -16,7 +17,7 @@
|
||||||
storedTitle.set('')
|
storedTitle.set('')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Head />
|
<Head page={{ title: 'Friends', path: '/friends' }} />
|
||||||
|
|
||||||
<Masonry
|
<Masonry
|
||||||
{items}
|
{items}
|
|
@ -1,32 +0,0 @@
|
||||||
import type { RequestHandler } from '@sveltejs/kit'
|
|
||||||
import { site } from '$lib/config/site'
|
|
||||||
import { any, maskable } from '$lib/config/icon'
|
|
||||||
|
|
||||||
export const GET: RequestHandler = () => ({
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/manifest+json; charset=utf-8'
|
|
||||||
},
|
|
||||||
body: JSON.stringify(
|
|
||||||
{
|
|
||||||
name: site.title,
|
|
||||||
short_name: site.title,
|
|
||||||
lang: site.lang,
|
|
||||||
description: site.description,
|
|
||||||
id: site.protocol + site.domain + '/',
|
|
||||||
start_url: '/',
|
|
||||||
scope: '/',
|
|
||||||
display: 'standalone',
|
|
||||||
orientation: 'portrait',
|
|
||||||
background_color: site.themeColor,
|
|
||||||
theme_color: site.themeColor,
|
|
||||||
icons: [
|
|
||||||
...Object.values(any)
|
|
||||||
.filter(icon => icon.sizes !== '180x180')
|
|
||||||
.map(icon => ({ ...icon, purpose: 'any' })),
|
|
||||||
...Object.values(maskable).map(icon => ({ ...icon, purpose: 'maskable' }))
|
|
||||||
]
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
})
|
|
37
src/routes/manifest.webmanifest/+server.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import type { RequestHandler } from './$types'
|
||||||
|
import { site } from '$lib/config/site'
|
||||||
|
import { any, maskable } from '$lib/config/icon'
|
||||||
|
|
||||||
|
export const prerender = true
|
||||||
|
export const trailingSlash = 'never'
|
||||||
|
export const GET: RequestHandler = () =>
|
||||||
|
new Response(
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
name: site.title,
|
||||||
|
short_name: site.title,
|
||||||
|
lang: site.lang,
|
||||||
|
description: site.description,
|
||||||
|
id: site.protocol + site.domain + '/',
|
||||||
|
start_url: '/',
|
||||||
|
scope: '/',
|
||||||
|
display: 'standalone',
|
||||||
|
orientation: 'portrait',
|
||||||
|
background_color: site.themeColor,
|
||||||
|
theme_color: site.themeColor,
|
||||||
|
icons: [
|
||||||
|
...Object.values(any)
|
||||||
|
.filter(icon => icon.sizes !== '180x180')
|
||||||
|
.map(icon => ({ ...icon, purpose: 'any' })),
|
||||||
|
...Object.values(maskable).map(icon => ({ ...icon, purpose: 'maskable' }))
|
||||||
|
]
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
),
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/manifest+json; charset=utf-8'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
|
@ -1,9 +0,0 @@
|
||||||
import type { RequestHandler } from '@sveltejs/kit'
|
|
||||||
import { genPosts } from '$lib/utils/posts'
|
|
||||||
|
|
||||||
export const GET: RequestHandler = async () => ({
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json; charset=utf-8'
|
|
||||||
},
|
|
||||||
body: JSON.stringify(genPosts(), null, 2)
|
|
||||||
})
|
|
7
src/routes/posts.json/+server.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import type { RequestHandler } from './$types'
|
||||||
|
import { json } from '@sveltejs/kit'
|
||||||
|
import { genPosts } from '$lib/utils/posts'
|
||||||
|
|
||||||
|
export const prerender = true
|
||||||
|
export const trailingSlash = 'never'
|
||||||
|
export const GET: RequestHandler = async () => json(genPosts())
|
|
@ -1,27 +0,0 @@
|
||||||
import type { RequestHandler } from '@sveltejs/kit'
|
|
||||||
import { site } from '$lib/config/site'
|
|
||||||
import { genPosts } from '$lib/utils/posts'
|
|
||||||
|
|
||||||
const render = async (): Promise<string> => `<?xml version='1.0' encoding='utf-8'?>
|
|
||||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
||||||
<url>
|
|
||||||
<loc>${site.protocol + site.domain}</loc>
|
|
||||||
</url>
|
|
||||||
${genPosts()
|
|
||||||
.map(
|
|
||||||
post => `
|
|
||||||
<url>
|
|
||||||
<loc>${site.protocol + site.domain + post.path}</loc>
|
|
||||||
<lastmod>${new Date(post.updated ?? post.published ?? post.created).toISOString()}</lastmod>
|
|
||||||
<priority>0.5</priority>
|
|
||||||
</url>`
|
|
||||||
)
|
|
||||||
.join('')}
|
|
||||||
</urlset>`
|
|
||||||
|
|
||||||
export const GET: RequestHandler = async () => ({
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/xml; charset=utf-8'
|
|
||||||
},
|
|
||||||
body: await render()
|
|
||||||
})
|
|
36
src/routes/sitemap.xml/+server.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import type { RequestHandler } from './$types'
|
||||||
|
import { site } from '$lib/config/site'
|
||||||
|
import { genPosts } from '$lib/utils/posts'
|
||||||
|
|
||||||
|
const render = (): string =>
|
||||||
|
`<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<urlset
|
||||||
|
xmlns="https://www.sitemaps.org/schemas/sitemap/0.9"
|
||||||
|
xmlns:xhtml="https://www.w3.org/1999/xhtml"
|
||||||
|
xmlns:mobile="https://www.google.com/schemas/sitemap-mobile/1.0"
|
||||||
|
xmlns:news="https://www.google.com/schemas/sitemap-news/0.9"
|
||||||
|
xmlns:image="https://www.google.com/schemas/sitemap-image/1.1"
|
||||||
|
xmlns:video="https://www.google.com/schemas/sitemap-video/1.1">
|
||||||
|
<url>
|
||||||
|
<loc>${site.protocol + site.domain}</loc>
|
||||||
|
</url>
|
||||||
|
${genPosts()
|
||||||
|
.map(
|
||||||
|
post => `
|
||||||
|
<url>
|
||||||
|
<loc>${site.protocol + site.domain + post.path}</loc>
|
||||||
|
<lastmod>${new Date(post.updated ?? post.published ?? post.created).toISOString()}</lastmod>
|
||||||
|
<priority>0.5</priority>
|
||||||
|
</url>`
|
||||||
|
)
|
||||||
|
.join('')}
|
||||||
|
</urlset>`.trim()
|
||||||
|
|
||||||
|
export const prerender = true
|
||||||
|
export const trailingSlash = 'never'
|
||||||
|
export const GET: RequestHandler = async () =>
|
||||||
|
new Response(render(), {
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/xml; charset=utf-8'
|
||||||
|
}
|
||||||
|
})
|
|
@ -1,9 +0,0 @@
|
||||||
import type { RequestHandler } from '@sveltejs/kit'
|
|
||||||
import { genPosts, genTags } from '$lib/utils/posts'
|
|
||||||
|
|
||||||
export const GET: RequestHandler = async () => ({
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json; charset=utf-8'
|
|
||||||
},
|
|
||||||
body: JSON.stringify(genTags(genPosts()), null, 2)
|
|
||||||
})
|
|
7
src/routes/tags.json/+server.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import type { RequestHandler } from './$types'
|
||||||
|
import { json } from '@sveltejs/kit'
|
||||||
|
import { genPosts, genTags } from '$lib/utils/posts'
|
||||||
|
|
||||||
|
export const prerender = true
|
||||||
|
export const trailingSlash = 'never'
|
||||||
|
export const GET: RequestHandler = async () => json(genTags(genPosts()))
|
|
@ -1,29 +1,35 @@
|
||||||
// sveltekit config type
|
// sveltekit config type
|
||||||
import type { Config } from '@sveltejs/kit'
|
import type { Config } from '@sveltejs/kit'
|
||||||
// svelte preprocess
|
// svelte adapter
|
||||||
import preprocess from 'svelte-preprocess'
|
import adapterVercel from '@sveltejs/adapter-vercel'
|
||||||
import adapterAuto from '@sveltejs/adapter-auto'
|
import adapterNetlify from '@sveltejs/adapter-netlify'
|
||||||
import adapterNode from '@sveltejs/adapter-node'
|
|
||||||
import adapterStatic from '@sveltejs/adapter-static'
|
import adapterStatic from '@sveltejs/adapter-static'
|
||||||
|
// svelte preprocessor
|
||||||
import { mdsvex } from 'mdsvex'
|
import { mdsvex } from 'mdsvex'
|
||||||
import mdsvexConfig from './mdsvex.config.js'
|
import mdsvexConfig from './mdsvex.config.js'
|
||||||
|
import { vitePreprocess } from '@sveltejs/kit/vite'
|
||||||
|
|
||||||
const defineConfig = (config: Config) => config
|
export default {
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
extensions: ['.svelte', ...(mdsvexConfig.extensions as string[])],
|
extensions: ['.svelte', ...(mdsvexConfig.extensions as string[])],
|
||||||
preprocess: [mdsvex(mdsvexConfig), preprocess()],
|
preprocess: [mdsvex(mdsvexConfig), vitePreprocess()],
|
||||||
kit: {
|
kit: {
|
||||||
adapter: Object.keys(process.env).some(key => ['VERCEL', 'CF_PAGES', 'NETLIFY'].includes(key))
|
adapter: Object.keys(process.env).some(key => key === 'VERCEL')
|
||||||
? adapterAuto()
|
? adapterVercel()
|
||||||
: process.env.ADAPTER === 'node'
|
: Object.keys(process.env).some(key => key === 'NETLIFY')
|
||||||
? adapterNode({ out: 'build' })
|
? adapterNetlify()
|
||||||
: adapterStatic({
|
: adapterStatic({
|
||||||
pages: 'build',
|
pages: 'build',
|
||||||
assets: 'build',
|
assets: 'build',
|
||||||
fallback: undefined
|
fallback: undefined
|
||||||
}),
|
}),
|
||||||
csp: { mode: 'auto' },
|
prerender: {
|
||||||
prerender: { default: true }
|
handleMissingId: 'warn'
|
||||||
|
},
|
||||||
|
csp: {
|
||||||
|
mode: 'auto',
|
||||||
|
directives: {
|
||||||
|
'style-src': ['self', 'unsafe-inline', 'https://giscus.app']
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
} as Config
|
||||||
|
|
|
@ -1,63 +1,12 @@
|
||||||
// tailwind config type
|
import { theme } from './src/lib/config/general'
|
||||||
import type { TailwindConfig } from 'tailwindcss/tailwind-config'
|
// @ts-ignore Could not find a declaration file for module '@tailwindcss/typography'.
|
||||||
// @ts-ignore TS2305: Module 'tailwindcss/plugin' has no exported member 'TailwindPluginWithoutOptions'.
|
|
||||||
import type { TailwindPluginWithoutOptions } from 'tailwindcss/plugin'
|
|
||||||
// tailwind plugins
|
|
||||||
import typography from '@tailwindcss/typography'
|
import typography from '@tailwindcss/typography'
|
||||||
|
// @ts-ignore Could not find a declaration file for module 'daisyui'.
|
||||||
import daisyui from 'daisyui'
|
import daisyui from 'daisyui'
|
||||||
|
|
||||||
interface Config extends TailwindConfig {
|
export default {
|
||||||
daisyui?: {
|
|
||||||
styled?: boolean
|
|
||||||
themes?: boolean | string[]
|
|
||||||
base?: boolean
|
|
||||||
utils?: boolean
|
|
||||||
logs?: boolean
|
|
||||||
rtl?: boolean
|
|
||||||
darkTheme?: string
|
|
||||||
prefix?: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const defineConfig = (config: Config) => config
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
content: ['./src/**/*.{html,md,js,svelte,ts}'],
|
content: ['./src/**/*.{html,md,js,svelte,ts}'],
|
||||||
theme: {
|
theme: { extend: {} },
|
||||||
extend: {}
|
plugins: [typography, daisyui],
|
||||||
},
|
daisyui: { themes: theme.map(({ name }) => name) }
|
||||||
plugins: [typography as TailwindPluginWithoutOptions, daisyui as TailwindPluginWithoutOptions],
|
}
|
||||||
daisyui: {
|
|
||||||
themes: [
|
|
||||||
'light',
|
|
||||||
'dark',
|
|
||||||
'cupcake',
|
|
||||||
'bumblebee',
|
|
||||||
'emerald',
|
|
||||||
'corporate',
|
|
||||||
'synthwave',
|
|
||||||
'retro',
|
|
||||||
'cyberpunk',
|
|
||||||
'valentine',
|
|
||||||
'halloween',
|
|
||||||
'garden',
|
|
||||||
'forest',
|
|
||||||
'aqua',
|
|
||||||
'lofi',
|
|
||||||
'pastel',
|
|
||||||
'fantasy',
|
|
||||||
'wireframe',
|
|
||||||
'black',
|
|
||||||
'luxury',
|
|
||||||
'dracula',
|
|
||||||
'cmyk',
|
|
||||||
'autumn',
|
|
||||||
'business',
|
|
||||||
'acid',
|
|
||||||
'lemonade',
|
|
||||||
'night',
|
|
||||||
'coffee',
|
|
||||||
'winter'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
4
urara.ts
|
@ -9,7 +9,7 @@ import chokidar from 'chokidar'
|
||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
extensions: ['svelte', 'md', 'js', 'ts'],
|
extensions: ['md'],
|
||||||
catch: ['ENOENT', 'EEXIST']
|
catch: ['ENOENT', 'EEXIST']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ const clean = () => {
|
||||||
switch (process.argv[2]) {
|
switch (process.argv[2]) {
|
||||||
case 'watch':
|
case 'watch':
|
||||||
{
|
{
|
||||||
let watcher = chokidar.watch('urara', {
|
const watcher = chokidar.watch('urara', {
|
||||||
ignored: (file: string) => path.basename(file).startsWith('.')
|
ignored: (file: string) => path.basename(file).startsWith('.')
|
||||||
})
|
})
|
||||||
watcher
|
watcher
|
||||||
|
|
27
urara/.well-known/webfinger
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"aliases": [
|
||||||
|
"https://kongwoo.icu/users/blog"
|
||||||
|
],
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"href": "https://kongwoo.icu/users/blog",
|
||||||
|
"rel": "http://webfinger.net/rel/profile-page",
|
||||||
|
"type": "text/html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "https://kongwoo.icu/users/blog",
|
||||||
|
"rel": "self",
|
||||||
|
"type": "application/activity+json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "https://kongwoo.icu/users/blog",
|
||||||
|
"rel": "self",
|
||||||
|
"type": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rel": "http://ostatus.org/schema/1.0/subscribe",
|
||||||
|
"template": "https://kongwoo.icu/ostatus_subscribe?acct={uri}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"subject": "acct:blog@kongwoo.icu"
|
||||||
|
}
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 219 KiB After Width: | Height: | Size: 80 KiB |
Before Width: | Height: | Size: 169 KiB After Width: | Height: | Size: 70 KiB |
|
@ -12,7 +12,7 @@ lastmod: 2022-04-16T12:54:20.049Z
|
||||||
|
|
||||||
## 书签这回事
|
## 书签这回事
|
||||||
|
|
||||||
上回说到用 [自建网页书签 Flare](https://seviche.cc/blog/flare/) ,今天不小心把 SSH 链接弄坏了(也就是连不上了),因为搭载的服务不多,所以把整个服务器都重装了,Flare 网页书签也炸了。
|
上回说到用 [自建网页书签 Flare](https://sevic.me/blog/flare/) ,今天不小心把 SSH 链接弄坏了(也就是连不上了),因为搭载的服务不多,所以把整个服务器都重装了,Flare 网页书签也炸了。
|
||||||
|
|
||||||
其实搭建之后我没有用过(一次都没有),平时的书签管理主要靠搜索,各个浏览器之间的书签互相导入后,直接在搜索栏搜,如果是常用的网址,我用 Chorme 扩展 [eesel](https://chrome.google.com/webstore/detail/eesel-productivity-at-wor/jffaiidojfhflballoapgofphkadiono) 来解决,它可以列出最近用过的网页,按站点分类,查找起来很方便
|
其实搭建之后我没有用过(一次都没有),平时的书签管理主要靠搜索,各个浏览器之间的书签互相导入后,直接在搜索栏搜,如果是常用的网址,我用 Chorme 扩展 [eesel](https://chrome.google.com/webstore/detail/eesel-productivity-at-wor/jffaiidojfhflballoapgofphkadiono) 来解决,它可以列出最近用过的网页,按站点分类,查找起来很方便
|
||||||
|
|
|
@ -2,16 +2,9 @@
|
||||||
title: Miniflux · 保存文章到 Pocket 以及 RSS
|
title: Miniflux · 保存文章到 Pocket 以及 RSS
|
||||||
summary: 将 Miniflux 上的文章到保存到 Pocket/Instapaper,以及 RSS 相关文章和资源
|
summary: 将 Miniflux 上的文章到保存到 Pocket/Instapaper,以及 RSS 相关文章和资源
|
||||||
created: 2022-03-10T16:24:38.663Z
|
created: 2022-03-10T16:24:38.663Z
|
||||||
preview: ''
|
|
||||||
draft: ''
|
|
||||||
tags:
|
tags:
|
||||||
- RSS
|
- RSS
|
||||||
- Miniflux
|
- Miniflux
|
||||||
changelogs:
|
|
||||||
- tag: '202203011'
|
|
||||||
summary:
|
|
||||||
- 添加了`instapaper`的连接方式
|
|
||||||
- 添加了Pocket按钮嵌入方式
|
|
||||||
lastmod: 2022-04-07T07:38:52.406Z
|
lastmod: 2022-04-07T07:38:52.406Z
|
||||||
---
|
---
|
||||||
|
|
Before Width: | Height: | Size: 197 KiB After Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |