Compare commits
191 commits
5612fc5cea
...
55648926cc
Author | SHA1 | Date | |
---|---|---|---|
|
55648926cc | ||
|
14eca348d5 | ||
|
ec77b7991c | ||
|
f89931db5e | ||
|
351defae29 | ||
|
7f991888c8 | ||
|
88c2451f1c | ||
|
44161293b1 | ||
|
6857d0db43 | ||
|
709a0ecced | ||
|
d2a4de22ec | ||
|
163636db1c | ||
|
fc3dc11a6c | ||
|
d26faa908c | ||
|
605955c529 | ||
|
688f43a179 | ||
|
cfbd3e80dc | ||
|
73ba867450 | ||
|
8184b22820 | ||
|
b111507444 | ||
|
c1f5625dfc | ||
|
75c75e6eb0 | ||
|
c625f93890 | ||
|
9246a9e62a | ||
|
caa4ee96ee | ||
|
bd40930dd1 | ||
|
5f0f13791f | ||
|
81b2dfe5eb | ||
|
044ee42fbd | ||
|
de1c154c5a | ||
|
bf1cf00372 | ||
|
e4c443f5de | ||
|
6b9f76c2f4 | ||
|
d608d52f96 | ||
|
b99e3bbce4 | ||
|
51001b4c9a | ||
|
bcbcc19cc4 | ||
|
c172e458dd | ||
|
d3d15f2319 | ||
|
893b52a54c | ||
|
c6ee95ed14 | ||
|
1a4d315dc8 | ||
|
f012cd49df | ||
|
3a101d930d | ||
|
d368b27ccb | ||
|
b55b7e743e | ||
|
f61de8a4ef | ||
|
3eee6f2e08 | ||
|
9a4524334b | ||
|
e6121d7ce6 | ||
|
5fa39eff5f | ||
|
dfe8c3c86b | ||
|
3b0f1e0054 | ||
|
bc1a63a3c6 | ||
|
ba77428863 | ||
|
6887d2d2f4 | ||
|
67fc487ebd | ||
|
76c25dca25 | ||
|
8e03d5a6f9 | ||
|
43fa4b6f1d | ||
|
5839788ec5 | ||
|
7caf081bf0 | ||
|
ce5cb61e4a | ||
|
89e26218da | ||
|
f88ed05c3a | ||
|
ea843494dd | ||
|
3b91c65936 | ||
|
40468ba170 | ||
|
34a9bb8bc7 | ||
|
09304b5ff2 | ||
|
fd852e8662 | ||
|
01ef65192c | ||
|
2dc0ae6a29 | ||
|
c62a1d3d9c | ||
|
c86396d231 | ||
|
8f026e2f82 | ||
|
97a7368f78 | ||
|
8f785854a2 | ||
|
2495dc7278 | ||
|
3bb83c69db | ||
|
6b0931e36f | ||
|
cccd540138 | ||
|
fd675ffba6 | ||
|
e1192c07c9 | ||
|
fcdd5e5b8b | ||
|
4a4437fa89 | ||
|
54ef29e9df | ||
|
3117fea376 | ||
|
f9900cf2ce | ||
|
6b9ddc6515 | ||
|
d9bab31169 | ||
|
7ca12e188f | ||
|
65742201d7 | ||
|
3dcc661557 | ||
|
412977735d | ||
|
fea2bb0eb7 | ||
|
46c00bd873 | ||
|
7db3e8e84c | ||
a820252f19 | |||
896ca068b7 | |||
ebcc44815d | |||
39101d900b | |||
93f88e280e | |||
60e4c8e2a9 | |||
656c3846b9 | |||
1fc6d7059c | |||
d794908974 | |||
0aab4b206c | |||
6c025d206a | |||
|
f6d906fbc5 | ||
|
a1229cc818 | ||
|
66f7be3031 | ||
|
dc2dde8179 | ||
|
f2a72ff665 | ||
|
a4b09a51b5 | ||
|
5ac936411d | ||
|
a552789045 | ||
|
8063db4288 | ||
|
66e70995ff | ||
|
78f03b17af | ||
|
8bb2dfba88 | ||
|
fb398f01e5 | ||
|
5fbfeb2a14 | ||
|
3da4beb5ac | ||
|
4a76821732 | ||
|
c2ee7651c8 | ||
|
51d0ccfc4d | ||
|
c5c84b6614 | ||
|
4dfc70fbfd | ||
|
51e83043c2 | ||
|
acdd20d4a8 | ||
|
a72c359538 | ||
|
f97821910f | ||
|
33eb87f572 | ||
|
33938da75c | ||
|
fac88146a3 | ||
|
b75c60446b | ||
|
f97af151ff | ||
|
26db89bc7e | ||
|
c0b4685c7d | ||
|
469f5cfa3c | ||
|
1dfadf2ca1 | ||
|
46eb1dcdde | ||
|
8182065dcb | ||
|
2e8a8d00f4 | ||
|
b79470639f | ||
|
50ca3f23fe | ||
|
28b408bcd4 | ||
|
3791f92a60 | ||
|
2a58c54703 | ||
|
f0b5baec3a | ||
|
ba8072ed97 | ||
|
20200c540f | ||
|
08949974ff | ||
|
16f0222a17 | ||
|
bc42a8069c | ||
|
da8707e219 | ||
|
9f3dd18a29 | ||
|
61d8b528bd | ||
|
aef66c2278 | ||
|
a7afea3b62 | ||
|
f41426b657 | ||
|
0e650c5e72 | ||
|
5ed354da07 | ||
|
2db40dc64a | ||
|
aa73a982d7 | ||
|
0cfe982419 | ||
|
b758269bcb | ||
|
e21f200413 | ||
|
d990868296 | ||
|
ba9f0cb316 | ||
|
2318fbb0a4 | ||
|
939dd01dcb | ||
|
a6014f2701 | ||
|
0a2e7d6c4d | ||
|
d5e0938a4f | ||
|
68da2b2c4d | ||
|
e3d3106970 | ||
|
1514bc87c0 | ||
|
ee25a10322 | ||
|
eb8bffb49b | ||
|
2f0f551806 | ||
|
2aa93f55f8 | ||
|
a83280d7c7 | ||
|
1c13a53032 | ||
|
65cf431855 | ||
|
381f3b952b | ||
|
64ad865a62 | ||
|
c14ed1c151 | ||
|
21021e8fe1 | ||
|
f7bd62db4d |
35
.gitignore
vendored
|
@ -1,15 +1,26 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
.svelte-kit
|
||||
/package
|
||||
src/routes/**/
|
||||
src/routes/*.md
|
||||
static/
|
||||
# build output
|
||||
build
|
||||
.vercel_build_output/
|
||||
.netlify/
|
||||
.env.local
|
||||
.env.**.local
|
||||
myblog/urara/2022-06-12-appwrite.md
|
||||
static
|
||||
.svelte-kit
|
||||
.netlify
|
||||
.vercel
|
||||
|
||||
|
||||
# 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
|
1
.npmrc
|
@ -1 +1,2 @@
|
|||
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,
|
||||
"files.eol": "\n",
|
||||
"typescript.tsdk": "node_modules\\typescript\\lib",
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"css.lint.unknownAtRules": "ignore",
|
||||
"svelte.plugin.css.diagnostics.enable": false,
|
||||
"[html]": {
|
||||
|
|
|
@ -20,7 +20,7 @@ My Tech Blog, base on [Urara](https://github.com/importantimport/urara)
|
|||
## TODO
|
||||
|
||||
- [ ] Note Page
|
||||
- [ ] Archie Page
|
||||
- [ ] Archvie Page
|
||||
- [ ] Refactoring the atom feed format
|
||||
- [ ] NeoDB component
|
||||
- [ ] ...
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#!/bin/bash
|
||||
echo "————————1. Build locally————————"
|
||||
netlify build
|
||||
echo "————————2.Deploy test————————"
|
||||
netlify deploy
|
||||
echo "————————3.Deploy————————"
|
||||
echo "————————2.Deploy————————————————"
|
||||
netlify deploy --prod
|
|
@ -13,6 +13,7 @@ import { parse, join } from 'path'
|
|||
import { visit } from 'unist-util-visit'
|
||||
import { toString } from 'mdast-util-to-string'
|
||||
import Slugger from 'github-slugger'
|
||||
import remarkFFF from 'remark-fff'
|
||||
import remarkFootnotes from 'remark-footnotes'
|
||||
|
||||
// highlighter
|
||||
|
@ -24,12 +25,12 @@ type VALUE = { [key in string | number]: VALUE } | Array<VALUE> | string | boole
|
|||
const remarkUraraFm =
|
||||
() =>
|
||||
(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)
|
||||
if (!data.fm) data.fm = {}
|
||||
// Generate slug & path
|
||||
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
|
||||
if (data.fm.toc !== false) {
|
||||
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)
|
||||
})
|
||||
})
|
||||
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
|
||||
|
@ -72,9 +59,7 @@ const remarkUraraSpoiler = () => (tree: Node<Data>) =>
|
|||
return node
|
||||
})
|
||||
|
||||
const defineConfig = (config: MdsvexOptions) => config
|
||||
|
||||
export default defineConfig({
|
||||
export default {
|
||||
extensions: ['.svelte.md', '.md'],
|
||||
smartypants: {
|
||||
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: [
|
||||
rehypeSlug,
|
||||
rehypeSlug as any,
|
||||
[rehypeAutolinkHeadings, { behavior: 'wrap' }],
|
||||
[
|
||||
rehypeExternalLinks,
|
||||
|
@ -116,4 +116,4 @@ export default defineConfig({
|
|||
}
|
||||
]
|
||||
]
|
||||
})
|
||||
} as MdsvexOptions
|
||||
|
|
15
netlify.toml
|
@ -1,8 +1,7 @@
|
|||
[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"
|
||||
|
||||
|
||||
[build.environment]
|
||||
NPM_FLAGS = "--version"
|
||||
AWS_LAMBDA_JS_RUNTIME = "nodejs16.x"
|
||||
|
@ -26,13 +25,17 @@ node_bundler = "esbuild"
|
|||
[[headers]]
|
||||
for = "/*"
|
||||
[headers.values]
|
||||
Access-Control-Allow-Origin = "https://seviche.cc"
|
||||
X-Frame-Options = "DENY"
|
||||
Access-Control-Allow-Origin = "sevic.me"
|
||||
X-Frame-Options = "SAMEORIGIN"
|
||||
X-Content-Type-Options = "nosniff"
|
||||
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"
|
||||
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]]
|
||||
from = "/.well-known/host-meta"
|
||||
|
|
102
package.json
|
@ -12,16 +12,12 @@
|
|||
"tsc:watch": "tsc -w -p tsconfig.node.json",
|
||||
"urara:build": "node urara.js build",
|
||||
"urara:watch": "node urara.js watch",
|
||||
"kit:dev": "cross-env NODE_OPTIONS=--max_old_space_size=7680 vite dev",
|
||||
"kit:build": "cross-env NODE_OPTIONS=--max_old_space_size=7680 vite build",
|
||||
"kit:dev": "cross-env NODE_OPTIONS=--max_old_space_size=11474 vite dev",
|
||||
"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": "npm-run-all -s tsc \"dev:parallel {@} \" --",
|
||||
"build": "npm-run-all -s tsc urara:build kit:build clean",
|
||||
"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:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
|
||||
|
@ -29,58 +25,66 @@
|
|||
"zhlint": "zhlint urara/*/*.md --fix && zhlint urara/*.md --fix"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/heroicons-outline": "^1.1.2",
|
||||
"@iconify-json/heroicons-solid": "^1.1.2",
|
||||
"@iconify-json/ic": "^1.1.9",
|
||||
"@iconify-json/simple-icons": "1.1.21",
|
||||
"@iconify-json/material-symbols": "1.1.14",
|
||||
"@iconify-json/mdi": "^1.1.30",
|
||||
"@iconify-json/uil": "^1.1.2",
|
||||
"@sveltejs/adapter-auto": "1.0.0-next.64",
|
||||
"@sveltejs/adapter-node": "1.0.0-next.86",
|
||||
"@sveltejs/adapter-static": "1.0.0-next.39",
|
||||
"@sveltejs/kit": "1.0.0-next.405",
|
||||
"@tailwindcss/typography": "^0.5.4",
|
||||
"@types/node": "^18.7.8",
|
||||
"@iconify-json/heroicons-outline": "^1.1.6",
|
||||
"@iconify-json/heroicons-solid": "^1.1.7",
|
||||
"@iconify-json/ic": "^1.1.13",
|
||||
"@iconify-json/icon-park-twotone": "1.1.9",
|
||||
"@iconify-json/material-symbols": "1.1.41",
|
||||
"@iconify-json/mdi": "^1.1.52",
|
||||
"@iconify-json/simple-icons": "1.1.52",
|
||||
"@iconify-json/uil": "^1.1.4",
|
||||
"@sveltejs/adapter-netlify": "^2.0.7",
|
||||
"@sveltejs/adapter-static": "^2.0.2",
|
||||
"@sveltejs/adapter-vercel": "2.4.3",
|
||||
"@sveltejs/kit": "^1.19.0",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@types/node": "^20.2.5",
|
||||
"@types/unist": "^2.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.33.1",
|
||||
"@typescript-eslint/parser": "^5.33.1",
|
||||
"autoprefixer": "^10.4.8",
|
||||
"chalk": "^5.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.7",
|
||||
"@typescript-eslint/parser": "^5.59.7",
|
||||
"@unocss/extractor-svelte": "^0.51.13",
|
||||
"@vite-pwa/sveltekit": "^0.1.3",
|
||||
"chalk": "^5.2.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"cross-env": "^7.0.3",
|
||||
"cssnano": "^5.1.13",
|
||||
"daisyui": "^2.24.0",
|
||||
"eslint": "^8.22.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"daisyui": "^2.51.6",
|
||||
"eslint": "^8.41.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
"fenceparser": "^2.2.0",
|
||||
"fff-flavored-frontmatter": "~0.2.2",
|
||||
"github-slugger": "^1.4.0",
|
||||
"mdast-util-to-string": "^3.1.0",
|
||||
"fff-flavored-frontmatter": "~0.5.3",
|
||||
"github-slugger": "^2.0.0",
|
||||
"mdast-util-to-string": "^3.2.0",
|
||||
"mdsvex": "^0.10.6",
|
||||
"netlify-cli": "^16.3.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.4.16",
|
||||
"prettier": "^2.7.1",
|
||||
"prettier-plugin-svelte": "^2.7.0",
|
||||
"postcss": "^8.4.23",
|
||||
"postcss-lightningcss": "^0.7.0",
|
||||
"prettier": "^2.8.8",
|
||||
"prettier-plugin-svelte": "^2.10.0",
|
||||
"rehype-autolink-headings": "^6.1.1",
|
||||
"rehype-external-links": "^2.0.0",
|
||||
"rehype-slug": "^5.0.1",
|
||||
"remark": "^14.0.2",
|
||||
"rehype-external-links": "^2.1.0",
|
||||
"rehype-slug": "^5.1.0",
|
||||
"remark": "^14.0.3",
|
||||
"remark-fff": "~0.5.3",
|
||||
"remark-footnotes": "~2.0.0",
|
||||
"shiki-twoslash": "^3.1.0",
|
||||
"svelte": "^3.49.0",
|
||||
"rollup": "^3.23.0",
|
||||
"shiki-twoslash": "^3.1.2",
|
||||
"svelte": "^3.59.1",
|
||||
"svelte-bricks": "^0.1.7",
|
||||
"svelte-check": "^2.8.1",
|
||||
"svelte-preprocess": "^4.10.7",
|
||||
"svelte-typeahead": "^4.2.4",
|
||||
"tailwindcss": "^3.1.8",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "^4.7.4",
|
||||
"unist-util-visit": "^4.1.0",
|
||||
"unocss": "^0.45.8",
|
||||
"vite": "^3.0.9",
|
||||
"vite-plugin-pwa": "^0.12.3",
|
||||
"workbox-window": "^6.5.4"
|
||||
"svelte-check": "^3.4.3",
|
||||
"svelte-preprocess": "^5.0.4",
|
||||
"svelte-typeahead": "^4.4.1",
|
||||
"sveltekit-embed": "^0.0.12",
|
||||
"tailwindcss": "^3.3.2",
|
||||
"tslib": "^2.5.2",
|
||||
"typescript": "^5.0.4",
|
||||
"unist-util-visit": "^4.1.2",
|
||||
"unocss": "^0.51.13",
|
||||
"vite": "^4.3.9",
|
||||
"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" />
|
||||
|
||||
import { FFFBase, FFFExtra } from 'fff-flavored-frontmatter'
|
||||
import type { FFFBase, FFFMention } from 'fff-flavored-frontmatter'
|
||||
|
||||
interface ImportMetaEnv extends Readonly<Record<string, string>> {
|
||||
readonly URARA_SITE_PROTOCOL?: 'http://' | 'https://'
|
||||
readonly URARA_SITE_DOMAIN?: string
|
||||
}
|
||||
|
||||
|
@ -14,8 +15,8 @@ interface ImportMeta {
|
|||
declare global {
|
||||
namespace Urara {
|
||||
namespace Post {
|
||||
type Frontmatter = Omit<FFFBase, 'created' | 'updated' | 'image' | 'audio' | 'video' | 'flags'> &
|
||||
Pick<FFFExtra, 'in_reply_to'> & {
|
||||
type Frontmatter = Omit<FFFBase, 'flags'> &
|
||||
Pick<FFFMention, 'in_reply_to'> & {
|
||||
/**
|
||||
* post type.
|
||||
* @remarks auto-generated
|
||||
|
@ -50,6 +51,10 @@ declare global {
|
|||
* @remarks auto-generated or set manually
|
||||
*/
|
||||
updated: string
|
||||
/**
|
||||
* the published date of the post.
|
||||
*/
|
||||
published?: string
|
||||
/**
|
||||
* the featured image for article, or image for "photo" / "multi-photo" posts.
|
||||
* @remarks currently only supports string
|
||||
|
|
|
@ -13,5 +13,6 @@
|
|||
|
||||
<body itemscope itemtype="https://schema.org/WebPage">
|
||||
%sveltekit.body%
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -56,7 +56,12 @@ html {
|
|||
/* .urara-prose 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 */
|
||||
|
@ -188,9 +193,9 @@ pre.twoslash data-lsp:hover::before {
|
|||
|
||||
.urara-prose blockquote:before {
|
||||
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 {
|
||||
@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
|
||||
</script>
|
||||
|
||||
<a
|
||||
href={`https://translate.google.com/translate?sl=auto&tl=${
|
||||
navigator.languages ? navigator.languages[0] : navigator.language
|
||||
}&u=${site.protocol + site.domain + post.path}`}
|
||||
class="btn btn-lg btn-circle btn-ghost bg-base-100 shadow-lg hover:shadow-xl">
|
||||
<span class="i-heroicons-outline-translate" />
|
||||
</a>
|
||||
<button class="tooltip tooltip-left opacity-60 hover:opacity-100" data-tip="Translate">
|
||||
<a
|
||||
href={`https://translate.google.com/translate?sl=auto&tl=${
|
||||
navigator.languages ? navigator.languages[0] : navigator.language
|
||||
}&u=${site.protocol + site.domain + post.path}`}
|
||||
class="btn btn-circle btn-ghost">
|
||||
<span class="i-heroicons-outline-translate" />
|
||||
</a>
|
||||
</button>
|
||||
|
|
|
@ -3,10 +3,12 @@
|
|||
export let post: Urara.Post
|
||||
</script>
|
||||
|
||||
<a
|
||||
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" />
|
||||
</a>
|
||||
<button class="tooltip tooltip-left opacity-60 hover:opacity-100" data-tip="Share">
|
||||
<a
|
||||
href={`https://www.addtoany.com/share#url=${site.protocol + site.domain + post.path}&title=${encodeURI(
|
||||
post.title ?? post.path.slice(1)
|
||||
)}`}
|
||||
class="btn btn-circle btn-ghost">
|
||||
<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>
|
||||
|
||||
<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">
|
||||
<p class="flex-1 m-auto italic opacity-50">
|
||||
<!-- {`Sort by=${config?.sortBy ?? 'Created'}&sort-dir=${sortDirUp ? 'up' : 'down'}`} -->
|
||||
|
@ -172,34 +202,4 @@
|
|||
{:else}
|
||||
<button id="webmention-loading" class="btn btn-lg btn-block flex btn-ghost loading" />
|
||||
{/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>
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
.then(
|
||||
({ 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))
|
||||
$: if (input)
|
||||
input.length < 5 ? (status = '') : input.includes('@') && input.includes('.') ? (status = 'success') : (status = 'warning')
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export let user = undefined
|
||||
export let repo = undefined
|
||||
export let user = ''
|
||||
export let repo = ''
|
||||
let info: {
|
||||
html_url: string
|
||||
description: string
|
||||
|
@ -20,7 +20,7 @@
|
|||
})
|
||||
</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">
|
||||
{#key info}
|
||||
{#if info}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { dev } from '$app/env'
|
||||
import { dev } from '$app/environment'
|
||||
let className = undefined
|
||||
export { className as class }
|
||||
export let src = undefined
|
||||
|
|
|
@ -19,10 +19,10 @@
|
|||
<div class="rounded-full border-2 border-white shadow-xl w-16 h-16">
|
||||
<img
|
||||
class="hover:rotate-[360deg] transition-transform duration-1000 ease-in-out m-0"
|
||||
src={avatar ?? site.author.avatar}
|
||||
alt={name ?? site.author.name}
|
||||
loading="lazy"
|
||||
decoding="async" />
|
||||
decoding="async"
|
||||
src={avatar ?? site.author.avatar} />
|
||||
</div>
|
||||
</div>
|
||||
{#if subname}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<div class="card-body p-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">
|
||||
<img class="rounded-md " src={project.img} alt={project.description} />
|
||||
<img class="rounded-md" src={project.img} alt={project.description} />
|
||||
</div>
|
||||
<div class="card-title flex-1 flex-col items-start gap-4">
|
||||
<div>
|
||||
|
|
|
@ -1,22 +1,26 @@
|
|||
<script lang="ts">
|
||||
export let id = undefined
|
||||
export let list = undefined
|
||||
export let playlist = undefined
|
||||
export let start = undefined
|
||||
export let autoplay = false
|
||||
export let disablekb = false
|
||||
export let controls = true
|
||||
export let id: string
|
||||
export let list: boolean | undefined = undefined
|
||||
export let playlist: string | undefined = undefined
|
||||
export let start: string | undefined = undefined
|
||||
export let autoplay: boolean = false
|
||||
export let disablekb: boolean = false
|
||||
export let controls: boolean = true
|
||||
export let fs = true
|
||||
export let loop = false
|
||||
|
||||
const src = `https://www.youtube.com/embed/${id}?${list ? `listType=playlist&list=${list}&` : ''}${
|
||||
playlist ? `playlist=${playlist}&` : ''
|
||||
}${start ? `start=${start}` : ''}${autoplay ? 'autoplay=1&' : ''}${disablekb ? 'disablekb=1&' : ''}${
|
||||
controls ? '' : 'controls=0&'
|
||||
}${fs ? '' : 'fs=0&'}${loop ? 'loop=1' : ''}`
|
||||
const src = `https://www.youtube.com/embed/${id}?${new URLSearchParams({
|
||||
...(list ? { listType: 'playlist', list: 'true' } : {}),
|
||||
...(playlist ? { playlist } : {}),
|
||||
...(start ? { start } : {}),
|
||||
autoplay: autoplay ? '1' : '0',
|
||||
disablekb: disablekb ? '1' : '0',
|
||||
controls: controls ? '1' : '0',
|
||||
fs: fs ? '1' : '0',
|
||||
loop: loop ? '1' : '0'
|
||||
}).toString()}`
|
||||
</script>
|
||||
|
||||
<div class="relative pb-[56.25%] mb-2">
|
||||
<div class="relative pb-[56.25%] mb-4">
|
||||
<iframe
|
||||
{src}
|
||||
class="absolute w-full h-full"
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<p>
|
||||
{#if footerConfig.nav}
|
||||
{#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}
|
||||
<span class="mr-1">·</span>
|
||||
{/if}
|
||||
|
@ -30,7 +30,7 @@
|
|||
<br />
|
||||
Powered by
|
||||
<a
|
||||
rel="noopener external"
|
||||
rel="noopener noreferrer external"
|
||||
target="_blank"
|
||||
class="tooltip tooltip-secondary hover:text-secondary"
|
||||
data-tip="🌸 [δ] - Based on MDsveX & SvelteKit 🌸"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { dev } from '$app/env'
|
||||
import { dev } from '$app/environment'
|
||||
import { head } from '$lib/config/general'
|
||||
import { site } from '$lib/config/site'
|
||||
import OpenGraph from '$lib/components/head_opengraph.svelte'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { browser, dev } from '$app/env'
|
||||
import { browser, dev } from '$app/environment'
|
||||
import { fly } from 'svelte/transition'
|
||||
import { site } from '$lib/config/site'
|
||||
import { theme } from '$lib/config/general'
|
||||
|
@ -41,7 +41,8 @@
|
|||
|
||||
if (browser)
|
||||
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>
|
||||
|
||||
<svelte:head>
|
||||
|
@ -54,60 +55,66 @@
|
|||
id="header"
|
||||
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 >
|
||||
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">
|
||||
<div class="navbar-start">
|
||||
{#if headerConfig.nav}
|
||||
<Nav {path} {title} {pin} {scrollY} nav={headerConfig.nav} />
|
||||
{/if}
|
||||
<a href="/" sveltekit:prefetch class="btn btn-ghost normal-case text-lg">{site.title}</a>
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
<!-- {#if headerConfig.search} -->
|
||||
<!-- The button to open modal -->
|
||||
<!-- <label for="search-modal" class="btn btn-square btn-ghost ml-2"><span class="i-heroicons-outline-search" /></label> -->
|
||||
<!-- <button
|
||||
on:click={() => {
|
||||
search = !search
|
||||
}}
|
||||
type="submit"
|
||||
class="btn btn-square btn-ghost ml-2">
|
||||
32 && 'backdrop-blur !border-base-content/10 bg-base-100/30 md:bg-base-200/30'}">
|
||||
{#if !search}
|
||||
<div in:fly={{ x: -50, duration: 300, delay: 300 }} out:fly={{ x: -50, duration: 300 }} class="navbar">
|
||||
<div class="navbar-start">
|
||||
{#if headerConfig.nav}
|
||||
<Nav {path} {title} {pin} {scrollY} nav={headerConfig.nav} />
|
||||
{/if}
|
||||
<a href="/" class="btn btn-ghost normal-case text-lg">{site.title}</a>
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
{#if headerConfig.search}
|
||||
<button aria-label="search" on:click={() => (search = !search)} tabindex="0" class="btn btn-square btn-ghost">
|
||||
<span class="i-heroicons-outline-search" />
|
||||
</button> -->
|
||||
<!-- {/if} -->
|
||||
<div id="change-theme" class="dropdown dropdown-end">
|
||||
<div tabindex="0" class="btn btn-square btn-ghost">
|
||||
<span class="i-heroicons-outline-color-swatch" />
|
||||
</button>
|
||||
{/if}
|
||||
<div id="change-theme" class="dropdown dropdown-end">
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<!-- 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>
|
||||
<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>
|
||||
{: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>
|
||||
|
||||
<button
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
export let pin: boolean
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||
<!-- reference: https://github.com/saadeghi/daisyui/issues/1285 -->
|
||||
<div class="dropdown lg:hidden">
|
||||
<label for="navbar-dropdown" tabindex="0" class="btn btn-square btn-ghost">
|
||||
<span class="i-heroicons-outline-menu-alt-1" />
|
||||
|
@ -14,11 +16,12 @@
|
|||
id="navbar-dropdown"
|
||||
tabindex="0"
|
||||
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 }}
|
||||
{#if link && !children}
|
||||
<li>
|
||||
<a sveltekit:prefetch class:font-bold={link === path} href={link}>{text}</a>
|
||||
<a class:font-bold={link === path} href={link}>{text}</a>
|
||||
</li>
|
||||
{:else if children}
|
||||
<li tabindex="0">
|
||||
|
@ -29,7 +32,7 @@
|
|||
<ul class="bg-base-100 text-base-content shadow-lg p-2">
|
||||
{#each children as { text, link }}
|
||||
<li>
|
||||
<a sveltekit:prefetch class:font-bold={link === path} href={link}>{text}</a>
|
||||
<a class:font-bold={link === path} href={link}>{text}</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
@ -49,18 +52,19 @@
|
|||
{#each nav as { text, link, children }}
|
||||
{#if link && !children}
|
||||
<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>
|
||||
{:else if children}
|
||||
<li tabindex="0">
|
||||
<span class:font-bold={children.some(({ link }) => link === path)} class="gap-1">
|
||||
<li>
|
||||
<span class:font-bold={children.some(({ link }) => link === path)} class="!rounded-btn gap-1">
|
||||
{text}
|
||||
<span class="i-heroicons-solid-chevron-down -mr-1" />
|
||||
</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 }}
|
||||
<li>
|
||||
<a sveltekit:prefetch class:font-bold={link === path} href={link}>{text}</a>
|
||||
<a class:font-bold={link === path} href={link}>{text}</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
|
|
@ -1,102 +1,21 @@
|
|||
<script lang="ts">
|
||||
import { onMount, createEventDispatcher } from 'svelte'
|
||||
import { site } from '$lib/config/site'
|
||||
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>
|
||||
|
||||
<form>
|
||||
<input type="checkbox" id="search-modal" class="modal-toggle " />
|
||||
<label for="search-modal" class="modal cursor-pointer w-full items-start pt-16 ">
|
||||
<label class="modal-box w-11/12 max-w-4xl " for="">
|
||||
<div class="form-control">
|
||||
<span
|
||||
class="i-heroicons-outline-search text-base-content pointer-events-none absolute z-10 my-2 ml-3 stroke-current opacity-60 w-5" />
|
||||
</div>
|
||||
<Typeahead placeholder="Search is not avaible …" limit={8} label="Search" inputAfterSelect="clear">
|
||||
<div class="py-1 text-sm" />
|
||||
</Typeahead>
|
||||
<div class="pointer-events-none absolute right-14 top-8 gap-1 opacity-50 hidden lg:flex">
|
||||
<kbd class="kbd kbd-sm">ESC</kbd>
|
||||
</div>
|
||||
</label>
|
||||
</label>
|
||||
<form
|
||||
action={headerConfig?.search?.provider === 'duckduckgo' ? '//duckduckgo.com/' : '//google.com/search'}
|
||||
method="get"
|
||||
class="flex-1">
|
||||
<input
|
||||
type="text"
|
||||
name="q"
|
||||
class="input input-ghost input-bordered xl:bg-base-100 xl:text-base-content transition-all w-full h-12" />
|
||||
<input
|
||||
type="hidden"
|
||||
name={headerConfig?.search?.provider === 'duckduckgo' ? 'sites' : 'sitesearch'}
|
||||
value={site.protocol + site.domain} />
|
||||
<button type="submit" class="btn btn-square btn-ghost ml-2">
|
||||
<span class="i-heroicons-outline-search" />
|
||||
</button>
|
||||
</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">
|
||||
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>
|
||||
|
||||
<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}
|
||||
{#each Object.values(actions) as action}
|
||||
<svelte:component this={action.default} {post} />
|
||||
<svelte:component this={action} {post} />
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { browser } from '$app/env'
|
||||
import { browser } from '$app/environment'
|
||||
import { post as postConfig } from '$lib/config/post'
|
||||
import { posts as storedPosts } from '$lib/stores/posts'
|
||||
import { title as storedTitle } from '$lib/stores/title'
|
||||
|
@ -35,11 +35,10 @@
|
|||
itemprop="blogPost"
|
||||
class:md:mb-8={!preview}
|
||||
class:lg:mb-16={!preview}
|
||||
class:h-entry={preview}
|
||||
class:group={preview}
|
||||
class:image-full={preview && post.type === 'article' && 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}
|
||||
<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}
|
||||
|
@ -73,9 +72,10 @@
|
|||
<div class="flex flex-col gap-2">
|
||||
{#if post.image && !preview}
|
||||
<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
|
||||
class={`${post.type === 'article' ? 'u-featured rounded-box shadow-xl' : 'u-photo'}`}
|
||||
class={`${post.type === 'article' ? 'u-featured' : 'u-photo'}`}
|
||||
src={post.image}
|
||||
alt={post.image}
|
||||
{loading}
|
||||
|
@ -91,7 +91,7 @@
|
|||
<a itemprop="url" class="u-url p-name" href={post.path}>{post.title ?? post.path.slice(1)}</a>
|
||||
</h2>
|
||||
{: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 post.summary}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import { toSnake } from '$lib/utils/case'
|
||||
export let post: Urara.Post
|
||||
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 currentConfig: unknown | undefined = undefined
|
||||
currentComment = localStorage.getItem('comment') ?? toSnake(config.use[0])
|
||||
|
@ -14,8 +14,9 @@
|
|||
{#if config?.use.length > 0}
|
||||
<div id="post-comment" class="card card-body">
|
||||
{#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}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<span
|
||||
on:click={() => {
|
||||
currentComment = toSnake(name)
|
||||
|
@ -33,7 +34,7 @@
|
|||
{#if currentComment}
|
||||
{#key currentComment}
|
||||
<svelte:component
|
||||
this={comments[`/src/lib/components/comments/${currentComment}.svelte`].default}
|
||||
this={comments[`/src/lib/components/comments/${currentComment}.svelte`]}
|
||||
{post}
|
||||
config={currentConfig} />
|
||||
{/key}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { fly } from 'svelte/transition'
|
||||
import { browser } from '$app/env'
|
||||
import { browser } from '$app/environment'
|
||||
import Card from '$lib/components/post_card.svelte'
|
||||
import Head from '$lib/components/head.svelte'
|
||||
import Toc from '$lib/components/post_toc.svelte'
|
||||
|
@ -22,7 +22,7 @@
|
|||
<div
|
||||
in:fly={{ x: 25, duration: 300, delay: 500 }}
|
||||
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}
|
||||
<Action {post} />
|
||||
{/if}
|
||||
|
@ -30,7 +30,7 @@
|
|||
<div
|
||||
in:fly={{ x: -25, duration: 300, delay: 500 }}
|
||||
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}
|
||||
<div class="h-full hidden xl:block">
|
||||
<Toc toc={post.toc} />
|
||||
|
|
|
@ -7,10 +7,9 @@
|
|||
<nav class="flex flex-col md:flex-row flex-warp justify-evenly">
|
||||
{#if prev}
|
||||
<div
|
||||
href={prev.path}
|
||||
class:image-full={prev['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']}
|
||||
<figure class="!block">
|
||||
<Image
|
||||
|
@ -34,10 +33,9 @@
|
|||
{/if}
|
||||
{#if next}
|
||||
<div
|
||||
href={next.path}
|
||||
class:image-full={next['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']}
|
||||
<figure class="!block">
|
||||
<Image
|
||||
|
|
|
@ -6,25 +6,12 @@
|
|||
|
||||
<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>
|
||||
{#if Array.isArray(in_reply_to)}
|
||||
{#each in_reply_to as reply}
|
||||
<a
|
||||
href={reply}
|
||||
rel="noopener external"
|
||||
target="_blank"
|
||||
class="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" />
|
||||
{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}
|
||||
<a
|
||||
href={in_reply_to}
|
||||
rel="noopener noreferrer 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>
|
||||
</div>
|
||||
|
|
|
@ -18,15 +18,15 @@
|
|||
{site.author.name}
|
||||
</a>
|
||||
<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
|
||||
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}
|
||||
itemprop="datePublished">
|
||||
{stringPublished}
|
||||
</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}
|
||||
itemprop="dateModified">
|
||||
{stringUpdated}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from 'svelte'
|
||||
import Tree from '$lib/components/post_toc_tree.svelte'
|
||||
export let toc: Urara.Post.Toc[]
|
||||
|
||||
let intersecting: string[] = []
|
||||
|
@ -34,7 +33,7 @@
|
|||
// @ts-ignore: Cannot find name 'headingsObserver'
|
||||
if (typeof headingsObserver !== 'undefined') headingsObserver.disconnect()
|
||||
// @ts-ignore: Cannot find name 'articleObserver'
|
||||
if (typeof headingsObserver !== 'undefined') articleObserver.disconnect()
|
||||
if (typeof articleObserver !== 'undefined') articleObserver.disconnect()
|
||||
})
|
||||
|
||||
$: if (intersecting.length > 0) bordered = intersecting
|
||||
|
@ -53,16 +52,29 @@
|
|||
aria-label="TableOfContent"
|
||||
dir="rtl"
|
||||
class="max-h-[calc(100vh-12rem)] overflow-y-hidden hover:overflow-y-auto">
|
||||
<Tree
|
||||
toc={toc.reduce(
|
||||
(acc, heading) => {
|
||||
let parent = acc
|
||||
// @ts-ignore Type 'Toc | undefined' is not assignable to type 'Toc.' ts(2322)
|
||||
while (parent.depth + 1 < heading.depth) parent = parent.children.at(-1)
|
||||
parent.children = [...(parent.children ?? []), { ...heading, children: [] }]
|
||||
return acc
|
||||
},
|
||||
{ depth: toc[0].depth - 1, children: [] }
|
||||
)} />
|
||||
<ul dir="ltr" id="toc-list-root">
|
||||
{#each toc as { depth, title, slug }}
|
||||
<li id={`toc-item-${slug}`} class="flex flex-col">
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<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>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</nav>
|
||||
</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">
|
||||
/* @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
|
||||
export { className as class }
|
||||
export let src: string
|
||||
export let alt: string = src
|
||||
export let loading: 'eager' | 'lazy' = 'lazy'
|
||||
export let decoding: 'async' | 'sync' | 'auto' = 'async'
|
||||
let source: Image[] | undefined = sources[`/src/static${src}`]
|
||||
</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}
|
|
@ -57,14 +57,14 @@ export const friends: Friend[] = [
|
|||
rel: 'friend',
|
||||
name: '夏诤',
|
||||
title: 'SummberBlue',
|
||||
link: 'https://summerblue.space/',
|
||||
link: 'https://blog.summerrrrrr.blue',
|
||||
descr: '早睡早起身体好'
|
||||
},
|
||||
{
|
||||
id: 'loikin',
|
||||
rel: 'friend',
|
||||
name: 'Loikin',
|
||||
title: '此生未命名',
|
||||
title: '浣心',
|
||||
link: 'https://blog.loikein.one/',
|
||||
descr: '用爱和理性对抗荒谬',
|
||||
avatar: '/assets/loikin.png'
|
||||
|
@ -74,8 +74,42 @@ export const friends: Friend[] = [
|
|||
rel: 'friend',
|
||||
name: '鲨',
|
||||
title: '一只脆脆鲨',
|
||||
link: 'http://blog.sharktale.xyz/',
|
||||
link: 'https://woods.sharktale.xyz/',
|
||||
descr: '遇见一只脆脆鲨',
|
||||
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'
|
||||
|
||||
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',
|
||||
text: 'Lo-Fi'
|
||||
},
|
||||
{
|
||||
name: 'pastel',
|
||||
text: 'Pastel'
|
||||
},
|
||||
{
|
||||
name: 'fantasy',
|
||||
text: 'Fantasy'
|
||||
},
|
||||
{
|
||||
name: 'wirefream',
|
||||
text: 'Wireframe'
|
||||
},
|
||||
{
|
||||
name: 'black',
|
||||
text: 'Black'
|
||||
},
|
||||
{
|
||||
name: 'luxury',
|
||||
text: 'Luxury'
|
||||
text: 'Light'
|
||||
},
|
||||
{
|
||||
name: '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',
|
||||
text: 'Coffee'
|
||||
},
|
||||
{
|
||||
name: 'winter',
|
||||
text: 'Winter'
|
||||
name: 'cupcake',
|
||||
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
|
||||
'<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
|
||||
'<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
|
||||
'<meta name="baiduspider" content="noindex,noarchive">',
|
||||
// Microsub
|
||||
|
@ -160,12 +100,20 @@ export const header: HeaderConfig = {
|
|||
{
|
||||
text: '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'
|
||||
}
|
||||
],
|
||||
html: '<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">CC BY-NC-SA 4.0</a>'
|
||||
html: '<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">CC BY-NC-SA 4.0</a>',
|
||||
since: '2021'
|
||||
}
|
||||
|
||||
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',
|
||||
options: {
|
||||
year: 'numeric',
|
||||
|
|
|
@ -5,14 +5,14 @@ export const post: PostConfig = {
|
|||
post: ['mastodon']
|
||||
},
|
||||
comment: {
|
||||
use: ['Webmention', 'Giscus'],
|
||||
use: ['Remark42','Webmention', 'Giscus'],
|
||||
style: 'boxed',
|
||||
webmention: {
|
||||
username: 'seviche.cc',
|
||||
username: 'sevic.me',
|
||||
sortBy: 'created',
|
||||
sortDir: 'down',
|
||||
form: true,
|
||||
commentParade: true
|
||||
commentParade: false
|
||||
},
|
||||
giscus: {
|
||||
// src: 'https://giscus.kwaa.dev/client.js',
|
||||
|
@ -22,16 +22,10 @@ export const post: PostConfig = {
|
|||
categoryID: 'DIC_kwDOHSra4c4CO9ua',
|
||||
theme: 'light',
|
||||
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',
|
||||
// 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',
|
||||
name: 'Fokify ',
|
||||
|
|
|
@ -1,36 +1,32 @@
|
|||
import type { SiteConfig } from '$lib/types/site'
|
||||
|
||||
export const site: SiteConfig = {
|
||||
protocol: 'https://',
|
||||
domain: 'seviche.cc',
|
||||
title: 'Seviche.cc',
|
||||
subtitle: 'Tech / Code / Random Life',
|
||||
protocol: import.meta.env.URARA_SITE_PROTOCOL ?? import.meta.env.DEV ? 'http://' : 'https://',
|
||||
domain: 'sevic.me',
|
||||
title: 'sevic.me',
|
||||
subtitle: 'Random Frontend-Developer',
|
||||
lang: 'zh',
|
||||
description: 'Tech / Code / Random Life',
|
||||
description: 'Random Frontend-Developer',
|
||||
author: {
|
||||
name: '酸橘汁腌鱼',
|
||||
avatar: '/assets/avatar.jpg',
|
||||
name: 'Sevi.C',
|
||||
avatar: '/assets/avatar.png',
|
||||
status: '🖤',
|
||||
bio: ' Code / Tech <br> Living a Random Life ',
|
||||
bio: 'Full-stack wizard.',
|
||||
metadata: [
|
||||
{
|
||||
text: '',
|
||||
icon: 'i-mdi-github',
|
||||
link: 'https://github.com/sevichecc'
|
||||
},
|
||||
{
|
||||
text: '',
|
||||
icon: 'i-simple-icons-matrix',
|
||||
link: 'https://matrix.to/#/@seviche:kongwoo.icu'
|
||||
},
|
||||
{
|
||||
text: '',
|
||||
icon: 'i-heroicons-solid-key',
|
||||
link: 'https://keys.openpgp.org/vks/v1/by-fingerprint/76DF9F9CC0C3619AA12CB914AFF18B986818D8AD',
|
||||
link: '/assets/DDDDDDDD.asc',
|
||||
rel: 'pgpkey'
|
||||
},
|
||||
{
|
||||
text: '',
|
||||
icon: 'i-ic-twotone-rss-feed',
|
||||
link: '/atom.xml',
|
||||
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'
|
||||
}
|
||||
|
|
|
@ -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 = {
|
||||
bridgy?: {
|
||||
[kind: string]: ('fed' | 'mastodon' | 'flickr' | 'github' | 'twitter')[]
|
||||
|
@ -22,8 +15,7 @@ export type CommentConfig = {
|
|||
giscus?: GiscusConfig
|
||||
/** Utterances config, more at https://utteranc.es */
|
||||
utterances?: UtterancesConfig
|
||||
/** Waline config, more at https://waline.js.org/en/reference/component.html#texrenderer */
|
||||
waline?: WalineConfig
|
||||
remark42?: Remark42Config
|
||||
}
|
||||
|
||||
export type WebmentionConfig = {
|
||||
|
@ -81,39 +73,35 @@ export type UtterancesConfig = {
|
|||
theme?: string
|
||||
}
|
||||
|
||||
export type DisqusConfig = {
|
||||
shortname: string
|
||||
lang?: string
|
||||
}
|
||||
|
||||
// Ref:https://waline.js.org/reference/component.html
|
||||
export type WalineConfig = {
|
||||
/** Waline server address url */
|
||||
serverURL: string
|
||||
/** Article path id*/
|
||||
path?: string
|
||||
/** Display language. */
|
||||
lang?: string
|
||||
/** Emoji settings, for details see https://waline.js.org/en/guide/client/emoji.html */
|
||||
emoji?: (string | WalineEmojiInfo)[] | false
|
||||
/** Darkmode support */
|
||||
dark?: string | boolean
|
||||
/** Reviewer attributes. Optional values: 'nick', 'mail', 'link' */
|
||||
meta?: string[]
|
||||
/** Set required fields*/
|
||||
requiredMeta?: string[]
|
||||
/** login mode status */
|
||||
login?: string
|
||||
/** Comment word s limit. */
|
||||
wordLimit?: number | [number, number]
|
||||
/**number of comments per page. */
|
||||
pageSize?: number
|
||||
/** Custom image upload method. */
|
||||
imageUploader?: WalineImageUploader | false
|
||||
/** Code highlighting, use hanabi by default */
|
||||
highlighter?: WalineHighlighter | false
|
||||
/** Customize \TeX rendering */
|
||||
texRender?: WalineTexRenderer | false
|
||||
/** Whether show copyright and version in footer. */
|
||||
copyright?: boolean
|
||||
export type Remark42Config = {
|
||||
/** hostname of Remark42 server, same as REMARK_URL in backend config, e.g. "https://demo.remark42.com" */
|
||||
host: string
|
||||
/** the SITE that you passed to Remark42 instance on start of backend. (default: remark) */
|
||||
site_id?: string
|
||||
/** url to the page with comments*/
|
||||
url?: string
|
||||
/** an array of widgets that should be rendered on a page (default: ['embed'] )*/
|
||||
components?: ['embed' | 'last-comments' | 'counter']
|
||||
/** maximum number of comments that is rendered on mobile version (default: 15 )*/
|
||||
max_shown_comments?: number
|
||||
/** maximum number of comments in the last comments widget (default: 15 )*/
|
||||
max_last_comments?: number
|
||||
/** changes UI theme, (default: light )*/
|
||||
theme?: 'light' | 'dark'
|
||||
/** title for current comments page (default: document.title)*/
|
||||
page_title?: string
|
||||
/**
|
||||
* interface localization,
|
||||
* 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)
|
||||
* default: en
|
||||
*/
|
||||
locale?: 'en' | 'be' | 'bp' | 'bg' | 'zh' | 'fi' | 'fr' | 'de' | 'ja' | 'ko' | 'pl' | 'ru' | 'es' | 'tr' | 'ua' | 'it' | 'vi'
|
||||
/** enables email subscription (default: true) */
|
||||
show_email_subscription?: boolean
|
||||
/** enables RSS subscription, (default: true) */
|
||||
show_rss_subscription?: boolean
|
||||
/** minimized UI with basic info only, (default: false) */
|
||||
simple_view?: boolean
|
||||
/** hides footer with signature and links to Remark42,(default: false) */
|
||||
no_footer?: boolean
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import type { FFFAuthor } from 'fff-flavored-frontmatter'
|
||||
|
||||
export type SiteConfig = {
|
||||
/** @deprecated - use `description` instead */
|
||||
descr?: string
|
||||
/** site protocol. for example: `https://` */
|
||||
protocol: string
|
||||
/** 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,15 +2,14 @@
|
|||
import { onMount } from 'svelte'
|
||||
import { fly } from 'svelte/transition'
|
||||
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 { title as storedTitle } from '$lib/stores/title'
|
||||
import Head from '$lib/components/head.svelte'
|
||||
import Footer from '$lib/components/footer.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 RemoteFollow from '$lib/components/extra/follow.svelte'
|
||||
|
||||
let allPosts: Urara.Post[]
|
||||
let allTags: string[]
|
||||
|
@ -19,9 +18,7 @@
|
|||
|
||||
storedTitle.set('')
|
||||
|
||||
$: storedPosts.subscribe(
|
||||
storedPosts => (allPosts = (storedPosts as Urara.Post[]).filter(post => !post.flags?.includes('unlisted')))
|
||||
)
|
||||
$: storedPosts.subscribe(storedPosts => (allPosts = storedPosts.filter(post => !post.flags?.includes('unlisted'))))
|
||||
|
||||
$: storedTags.subscribe(storedTags => (allTags = storedTags as string[]))
|
||||
|
||||
|
@ -30,7 +27,7 @@
|
|||
$: if (tags) {
|
||||
posts = !tags ? allPosts : allPosts.filter(post => tags.every(tag => post.tags?.includes(tag)))
|
||||
if (browser && window.location.pathname === '/')
|
||||
window.history.replaceState({}, '', tags.length > 0 ? `?tags=${tags.toString()}` : `/`)
|
||||
goto(tags.length > 0 ? `?tags=${tags.toString()}` : `/`, { replaceState: true })
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
@ -125,5 +122,3 @@
|
|||
{/key}
|
||||
</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 { feed } from '$lib/config/general'
|
||||
import { favicon } from '$lib/config/icon'
|
||||
import { genPosts, genTags } from '$lib/utils/posts'
|
||||
|
||||
const render = async (
|
||||
posts = genPosts({ postHtml: true, postLimit: feed.limit, filterUnlisted: true })
|
||||
): Promise<string> => `<?xml version='1.0' encoding='utf-8'?>
|
||||
const render = (posts = genPosts({ postHtml: true, postLimit: feed.limit, filterUnlisted: true })): string =>
|
||||
`<?xml version='1.0' encoding='utf-8'?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<id>${site.protocol + site.domain}/</id>
|
||||
<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}/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>
|
||||
<author>
|
||||
<name><![CDATA[${site.author.name}]]></name>
|
||||
</author>${genTags(posts)
|
||||
.map(tag => `\n <category term="${tag}" scheme="${site.protocol + site.domain}/?tags=${encodeURI(tag)}" />`)
|
||||
.join('')}${posts
|
||||
.map(
|
||||
post => `\n <entry>
|
||||
.map(
|
||||
post => `\n <entry>
|
||||
<title type="html"><![CDATA[${post.title}]]></title>
|
||||
<link href="${site.protocol + site.domain + post.path}" />
|
||||
<id>${site.protocol + site.domain + post.path}</id>
|
||||
<published>${new Date(post.published ?? post.created).toJSON()}</published>
|
||||
<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">
|
||||
<![CDATA[${post.html}]]>
|
||||
</content>${post.tags
|
||||
?.map(tag => `\n <category term="${tag}" scheme="${site.protocol + site.domain}/?tags=${encodeURI(tag)}" />`)
|
||||
.join('')}
|
||||
</entry>`
|
||||
)
|
||||
.join('')}
|
||||
</feed>`
|
||||
)
|
||||
.join('')}
|
||||
</feed>`.trim()
|
||||
|
||||
export const GET: RequestHandler = async () => ({
|
||||
headers: {
|
||||
'Content-Type': 'application/atom+xml; charset=utf-8'
|
||||
},
|
||||
body: await render()
|
||||
})
|
||||
export const prerender = true
|
||||
export const trailingSlash = 'never'
|
||||
export const GET: RequestHandler = async () =>
|
||||
new Response(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 { feed } from '$lib/config/general'
|
||||
import { favicon, any } from '$lib/config/icon'
|
||||
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',
|
||||
title: site.title,
|
||||
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 () => ({
|
||||
headers: {
|
||||
'Content-Type': 'application/feed+json; charset=utf-8'
|
||||
},
|
||||
body: JSON.stringify(await render(), null, 2)
|
||||
})
|
||||
export const prerender = true
|
||||
export const trailingSlash = 'never'
|
||||
export const GET: RequestHandler = async () =>
|
||||
json(render(), {
|
||||
headers: {
|
||||
'content-type': 'application/feed+json; charset=utf-8'
|
||||
}
|
||||
})
|
|
@ -1,9 +1,10 @@
|
|||
<script lang="ts">
|
||||
// @ts-nocheck
|
||||
import Masonry from 'svelte-bricks'
|
||||
import type { Friend } from '$lib/config/friends'
|
||||
import { friends as allFriends } from '$lib/config/friends'
|
||||
import { title as storedTitle } from '$lib/stores/title'
|
||||
import Head from '$lib/components/head.svelte'
|
||||
import Masonry from 'svelte-bricks'
|
||||
import FriendComponent from '$lib/components/extra/friend.svelte'
|
||||
|
||||
const rnd = Math.random()
|
||||
|
@ -16,7 +17,7 @@
|
|||
storedTitle.set('')
|
||||
</script>
|
||||
|
||||
<Head />
|
||||
<Head page={{ title: 'Friends', path: '/friends' }} />
|
||||
|
||||
<Masonry
|
||||
{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
|
||||
import type { Config } from '@sveltejs/kit'
|
||||
// svelte preprocess
|
||||
import preprocess from 'svelte-preprocess'
|
||||
import adapterAuto from '@sveltejs/adapter-auto'
|
||||
import adapterNode from '@sveltejs/adapter-node'
|
||||
// svelte adapter
|
||||
import adapterVercel from '@sveltejs/adapter-vercel'
|
||||
import adapterNetlify from '@sveltejs/adapter-netlify'
|
||||
import adapterStatic from '@sveltejs/adapter-static'
|
||||
// svelte preprocessor
|
||||
import { mdsvex } from 'mdsvex'
|
||||
import mdsvexConfig from './mdsvex.config.js'
|
||||
import { vitePreprocess } from '@sveltejs/kit/vite'
|
||||
|
||||
const defineConfig = (config: Config) => config
|
||||
|
||||
export default defineConfig({
|
||||
export default {
|
||||
extensions: ['.svelte', ...(mdsvexConfig.extensions as string[])],
|
||||
preprocess: [mdsvex(mdsvexConfig), preprocess()],
|
||||
preprocess: [mdsvex(mdsvexConfig), vitePreprocess()],
|
||||
kit: {
|
||||
adapter: Object.keys(process.env).some(key => ['VERCEL', 'CF_PAGES', 'NETLIFY'].includes(key))
|
||||
? adapterAuto()
|
||||
: process.env.ADAPTER === 'node'
|
||||
? adapterNode({ out: 'build' })
|
||||
adapter: Object.keys(process.env).some(key => key === 'VERCEL')
|
||||
? adapterVercel()
|
||||
: Object.keys(process.env).some(key => key === 'NETLIFY')
|
||||
? adapterNetlify()
|
||||
: adapterStatic({
|
||||
pages: 'build',
|
||||
assets: 'build',
|
||||
fallback: undefined
|
||||
}),
|
||||
csp: { mode: 'auto' },
|
||||
prerender: { default: true }
|
||||
prerender: {
|
||||
handleMissingId: 'warn'
|
||||
},
|
||||
csp: {
|
||||
mode: 'auto',
|
||||
directives: {
|
||||
'style-src': ['self', 'unsafe-inline', 'https://giscus.app']
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
} as Config
|
||||
|
|
|
@ -1,63 +1,12 @@
|
|||
// tailwind config type
|
||||
import type { TailwindConfig } from 'tailwindcss/tailwind-config'
|
||||
// @ts-ignore TS2305: Module 'tailwindcss/plugin' has no exported member 'TailwindPluginWithoutOptions'.
|
||||
import type { TailwindPluginWithoutOptions } from 'tailwindcss/plugin'
|
||||
// tailwind plugins
|
||||
import { theme } from './src/lib/config/general'
|
||||
// @ts-ignore Could not find a declaration file for module '@tailwindcss/typography'.
|
||||
import typography from '@tailwindcss/typography'
|
||||
// @ts-ignore Could not find a declaration file for module 'daisyui'.
|
||||
import daisyui from 'daisyui'
|
||||
|
||||
interface Config extends TailwindConfig {
|
||||
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({
|
||||
export default {
|
||||
content: ['./src/**/*.{html,md,js,svelte,ts}'],
|
||||
theme: {
|
||||
extend: {}
|
||||
},
|
||||
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'
|
||||
]
|
||||
}
|
||||
})
|
||||
theme: { extend: {} },
|
||||
plugins: [typography, daisyui],
|
||||
daisyui: { themes: theme.map(({ name }) => name) }
|
||||
}
|
||||
|
|
4
urara.ts
|
@ -9,7 +9,7 @@ import chokidar from 'chokidar'
|
|||
import chalk from 'chalk'
|
||||
|
||||
const config = {
|
||||
extensions: ['svelte', 'md', 'js', 'ts'],
|
||||
extensions: ['md'],
|
||||
catch: ['ENOENT', 'EEXIST']
|
||||
}
|
||||
|
||||
|
@ -102,7 +102,7 @@ const clean = () => {
|
|||
switch (process.argv[2]) {
|
||||
case 'watch':
|
||||
{
|
||||
let watcher = chokidar.watch('urara', {
|
||||
const watcher = chokidar.watch('urara', {
|
||||
ignored: (file: string) => path.basename(file).startsWith('.')
|
||||
})
|
||||
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) 来解决,它可以列出最近用过的网页,按站点分类,查找起来很方便
|
||||
|
|
@ -2,16 +2,9 @@
|
|||
title: Miniflux · 保存文章到 Pocket 以及 RSS
|
||||
summary: 将 Miniflux 上的文章到保存到 Pocket/Instapaper,以及 RSS 相关文章和资源
|
||||
created: 2022-03-10T16:24:38.663Z
|
||||
preview: ''
|
||||
draft: ''
|
||||
tags:
|
||||
- RSS
|
||||
- Miniflux
|
||||
changelogs:
|
||||
- tag: '202203011'
|
||||
summary:
|
||||
- 添加了`instapaper`的连接方式
|
||||
- 添加了Pocket按钮嵌入方式
|
||||
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 |