add source codes

This commit is contained in:
moris 2025-01-19 01:06:28 +09:00
parent 9da3d1044d
commit 1a26f0aaf4
55 changed files with 2774 additions and 0 deletions

6
.env.example Normal file
View file

@ -0,0 +1,6 @@
# Hostname
PUBLIC_HOSTNAME = 'moris.day'
# Post
POST_DIR = '/home/user/BlogPosts'
PUBLIC_POST_REPO = 'https://github.com/moris/Posts'

1
.npmrc Normal file
View file

@ -0,0 +1 @@
engine-strict=true

3
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"npm.packageManager": "bun",
}

7
Caddyfile Normal file
View file

@ -0,0 +1,7 @@
localhost:80, macbookair.local:80 {
file_server
handle_path /blog/* {
root build
}
}

BIN
bun.lockb Executable file

Binary file not shown.

735
package-lock.json generated Normal file
View file

@ -0,0 +1,735 @@
{
"name": "day.moris.blog",
"version": "0.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "day.moris.blog",
"version": "0.0.1",
"devDependencies": {
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"typescript": "^5.0.0",
"vite": "^5.4.11"
}
},
"node_modules/@ampproject/remapping": {
"version": "2.3.0",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.24"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.21.5",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.8",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/set-array": "^1.2.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/trace-mapping": "^0.3.24"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/set-array": {
"version": "1.2.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.0",
"dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.25",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@polka/url": {
"version": "1.0.0-next.28",
"dev": true,
"license": "MIT"
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.29.1",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@sveltejs/adapter-auto": {
"version": "3.3.1",
"dev": true,
"license": "MIT",
"dependencies": {
"import-meta-resolve": "^4.1.0"
},
"peerDependencies": {
"@sveltejs/kit": "^2.0.0"
}
},
"node_modules/@sveltejs/adapter-static": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.8.tgz",
"integrity": "sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"@sveltejs/kit": "^2.0.0"
}
},
"node_modules/@sveltejs/kit": {
"version": "2.15.1",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@types/cookie": "^0.6.0",
"cookie": "^0.6.0",
"devalue": "^5.1.0",
"esm-env": "^1.2.1",
"import-meta-resolve": "^4.1.0",
"kleur": "^4.1.5",
"magic-string": "^0.30.5",
"mrmime": "^2.0.0",
"sade": "^1.8.1",
"set-cookie-parser": "^2.6.0",
"sirv": "^3.0.0",
"tiny-glob": "^0.2.9"
},
"bin": {
"svelte-kit": "svelte-kit.js"
},
"engines": {
"node": ">=18.13"
},
"peerDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0",
"svelte": "^4.0.0 || ^5.0.0-next.0",
"vite": "^5.0.3 || ^6.0.0"
}
},
"node_modules/@sveltejs/vite-plugin-svelte": {
"version": "4.0.4",
"dev": true,
"license": "MIT",
"dependencies": {
"@sveltejs/vite-plugin-svelte-inspector": "^3.0.0-next.0||^3.0.0",
"debug": "^4.3.7",
"deepmerge": "^4.3.1",
"kleur": "^4.1.5",
"magic-string": "^0.30.12",
"vitefu": "^1.0.3"
},
"engines": {
"node": "^18.0.0 || ^20.0.0 || >=22"
},
"peerDependencies": {
"svelte": "^5.0.0-next.96 || ^5.0.0",
"vite": "^5.0.0"
}
},
"node_modules/@sveltejs/vite-plugin-svelte-inspector": {
"version": "3.0.1",
"dev": true,
"license": "MIT",
"dependencies": {
"debug": "^4.3.7"
},
"engines": {
"node": "^18.0.0 || ^20.0.0 || >=22"
},
"peerDependencies": {
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.0||^4.0.0",
"svelte": "^5.0.0-next.96 || ^5.0.0",
"vite": "^5.0.0"
}
},
"node_modules/@types/cookie": {
"version": "0.6.0",
"dev": true,
"license": "MIT"
},
"node_modules/@types/estree": {
"version": "1.0.6",
"dev": true,
"license": "MIT"
},
"node_modules/acorn": {
"version": "8.14.0",
"dev": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/acorn-typescript": {
"version": "1.4.13",
"dev": true,
"license": "MIT",
"peerDependencies": {
"acorn": ">=8.9.0"
}
},
"node_modules/aria-query": {
"version": "5.3.2",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/axobject-query": {
"version": "4.1.0",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/chokidar": {
"version": "4.0.3",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/clsx": {
"version": "2.1.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/cookie": {
"version": "0.6.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/debug": {
"version": "4.4.0",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/deepmerge": {
"version": "4.3.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/devalue": {
"version": "5.1.1",
"dev": true,
"license": "MIT"
},
"node_modules/esbuild": {
"version": "0.21.5",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.21.5",
"@esbuild/android-arm": "0.21.5",
"@esbuild/android-arm64": "0.21.5",
"@esbuild/android-x64": "0.21.5",
"@esbuild/darwin-arm64": "0.21.5",
"@esbuild/darwin-x64": "0.21.5",
"@esbuild/freebsd-arm64": "0.21.5",
"@esbuild/freebsd-x64": "0.21.5",
"@esbuild/linux-arm": "0.21.5",
"@esbuild/linux-arm64": "0.21.5",
"@esbuild/linux-ia32": "0.21.5",
"@esbuild/linux-loong64": "0.21.5",
"@esbuild/linux-mips64el": "0.21.5",
"@esbuild/linux-ppc64": "0.21.5",
"@esbuild/linux-riscv64": "0.21.5",
"@esbuild/linux-s390x": "0.21.5",
"@esbuild/linux-x64": "0.21.5",
"@esbuild/netbsd-x64": "0.21.5",
"@esbuild/openbsd-x64": "0.21.5",
"@esbuild/sunos-x64": "0.21.5",
"@esbuild/win32-arm64": "0.21.5",
"@esbuild/win32-ia32": "0.21.5",
"@esbuild/win32-x64": "0.21.5"
}
},
"node_modules/esm-env": {
"version": "1.2.1",
"dev": true,
"license": "MIT"
},
"node_modules/esrap": {
"version": "1.3.2",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
}
},
"node_modules/fdir": {
"version": "6.4.2",
"dev": true,
"license": "MIT",
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/globalyzer": {
"version": "0.1.0",
"dev": true,
"license": "MIT"
},
"node_modules/globrex": {
"version": "0.1.2",
"dev": true,
"license": "MIT"
},
"node_modules/import-meta-resolve": {
"version": "4.1.0",
"dev": true,
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/is-reference": {
"version": "3.0.3",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.6"
}
},
"node_modules/kleur": {
"version": "4.1.5",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/locate-character": {
"version": "3.0.0",
"dev": true,
"license": "MIT"
},
"node_modules/magic-string": {
"version": "0.30.17",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0"
}
},
"node_modules/mri": {
"version": "1.2.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/mrmime": {
"version": "2.0.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/ms": {
"version": "2.1.3",
"dev": true,
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.8",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"dev": true,
"license": "ISC"
},
"node_modules/postcss": {
"version": "8.4.49",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/readdirp": {
"version": "4.0.2",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/rollup": {
"version": "4.29.1",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.6"
},
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=18.0.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.29.1",
"@rollup/rollup-android-arm64": "4.29.1",
"@rollup/rollup-darwin-arm64": "4.29.1",
"@rollup/rollup-darwin-x64": "4.29.1",
"@rollup/rollup-freebsd-arm64": "4.29.1",
"@rollup/rollup-freebsd-x64": "4.29.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.29.1",
"@rollup/rollup-linux-arm-musleabihf": "4.29.1",
"@rollup/rollup-linux-arm64-gnu": "4.29.1",
"@rollup/rollup-linux-arm64-musl": "4.29.1",
"@rollup/rollup-linux-loongarch64-gnu": "4.29.1",
"@rollup/rollup-linux-powerpc64le-gnu": "4.29.1",
"@rollup/rollup-linux-riscv64-gnu": "4.29.1",
"@rollup/rollup-linux-s390x-gnu": "4.29.1",
"@rollup/rollup-linux-x64-gnu": "4.29.1",
"@rollup/rollup-linux-x64-musl": "4.29.1",
"@rollup/rollup-win32-arm64-msvc": "4.29.1",
"@rollup/rollup-win32-ia32-msvc": "4.29.1",
"@rollup/rollup-win32-x64-msvc": "4.29.1",
"fsevents": "~2.3.2"
}
},
"node_modules/sade": {
"version": "1.8.1",
"dev": true,
"license": "MIT",
"dependencies": {
"mri": "^1.1.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/set-cookie-parser": {
"version": "2.7.1",
"dev": true,
"license": "MIT"
},
"node_modules/sirv": {
"version": "3.0.0",
"dev": true,
"license": "MIT",
"dependencies": {
"@polka/url": "^1.0.0-next.24",
"mrmime": "^2.0.0",
"totalist": "^3.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/svelte": {
"version": "5.16.0",
"dev": true,
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "^2.3.0",
"@jridgewell/sourcemap-codec": "^1.5.0",
"@types/estree": "^1.0.5",
"acorn": "^8.12.1",
"acorn-typescript": "^1.4.13",
"aria-query": "^5.3.1",
"axobject-query": "^4.1.0",
"clsx": "^2.1.1",
"esm-env": "^1.2.1",
"esrap": "^1.3.2",
"is-reference": "^3.0.3",
"locate-character": "^3.0.0",
"magic-string": "^0.30.11",
"zimmerframe": "^1.1.2"
},
"engines": {
"node": ">=18"
}
},
"node_modules/svelte-check": {
"version": "4.1.1",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.25",
"chokidar": "^4.0.1",
"fdir": "^6.2.0",
"picocolors": "^1.0.0",
"sade": "^1.7.4"
},
"bin": {
"svelte-check": "bin/svelte-check"
},
"engines": {
"node": ">= 18.0.0"
},
"peerDependencies": {
"svelte": "^4.0.0 || ^5.0.0-next.0",
"typescript": ">=5.0.0"
}
},
"node_modules/tiny-glob": {
"version": "0.2.9",
"dev": true,
"license": "MIT",
"dependencies": {
"globalyzer": "0.1.0",
"globrex": "^0.1.2"
}
},
"node_modules/totalist": {
"version": "3.0.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/typescript": {
"version": "5.7.2",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/vite": {
"version": "5.4.11",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
"rollup": "^4.20.0"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^18.0.0 || >=20.0.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
},
"peerDependencies": {
"@types/node": "^18.0.0 || >=20.0.0",
"less": "*",
"lightningcss": "^1.21.0",
"sass": "*",
"sass-embedded": "*",
"stylus": "*",
"sugarss": "*",
"terser": "^5.4.0"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"less": {
"optional": true
},
"lightningcss": {
"optional": true
},
"sass": {
"optional": true
},
"sass-embedded": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
}
}
},
"node_modules/vitefu": {
"version": "1.0.4",
"dev": true,
"license": "MIT",
"workspaces": [
"tests/deps/*",
"tests/projects/*"
],
"peerDependencies": {
"vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0"
},
"peerDependenciesMeta": {
"vite": {
"optional": true
}
}
},
"node_modules/zimmerframe": {
"version": "1.1.2",
"dev": true,
"license": "MIT"
}
}
}

39
package.json Normal file
View file

@ -0,0 +1,39 @@
{
"name": "day.moris.blog",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
},
"devDependencies": {
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"typescript": "^5.0.0",
"vite": "^5.4.11",
"@types/bun": "latest"
},
"dependencies": {
"@twemoji/parser": "^15.1.1",
"github-slugger": "^2.0.0",
"rehype-external-links": "^3.0.0",
"rehype-highlight": "^7.0.1",
"rehype-katex": "^7.0.1",
"rehype-slug": "^6.0.0",
"rehype-stringify": "^10.0.1",
"remark": "^15.0.1",
"remark-breaks": "^4.0.0",
"remark-gfm": "^4.0.0",
"remark-math": "^6.0.0",
"remark-rehype": "^11.1.1",
"yaml": "^2.6.1"
},
"module": "index.ts"
}

99
src/app.css Normal file
View file

@ -0,0 +1,99 @@
@property --theme-color {
syntax: "<color>";
inherits: true;
}
@property --back-color {
syntax: "<color>";
inherits: true;
}
@property --grid-color {
syntax: "<color>";
inherits: true;
}
/* theme color */
body, body.light {
--color-scheme: light;
--white-black: white;
--black-white: black;
--theme-color: hsl(85, 50%, 60%);
--back-color: hsl(60, 100%, 98%);
--font-color: #222;
--grid-color: #fff;
--code-color: #ddd;
--code-block: #30303a;
--frame-shadow: #999;
}
body.dark {
--color-scheme: dark;
--white-black: black;
--black-white: white;
--theme-color: teal;
--back-color: hsl(210, 7%, 18%);
--font-color: #f5f5f5;
--grid-color: #333;
--code-color: #445;
--code-block: #23232a;
--frame-shadow: #111;
}
@media(prefers-color-scheme: dark){
body {
--color-scheme: dark;
--white-black: black;
--black-white: white;
--theme-color: teal;
--back-color: hsl(210, 7%, 18%);
--font-color: #fafafa;
--grid-color: #333;
--code-color: #445;
--code-block: #23232a;
--frame-shadow: #111;
}
}
/* general */
body {
margin: 0;
color: var(--font-color);
}
img {
-webkit-user-drag: none;
user-select: none;
}
@media (scripting: none) {
.js {display: none !important;}
}
/* font */
body {
font-family: "-apple-system", "BlinkMacSystemFont", "Hiragino Kaku Gothic ProN", "Noto Sans CJK JP", "Segoe UI", "BIZ UDPGothic", sans-serif;
/* Apple | Linux | win-en win-jp */
}
code {
font-family: "Source Code Pro", "Monaco", "Consolas", "BIZ UDGothic", monospace;
/* Linux | Apple | windows | 和文 */
}
em {
font-family: serif;
}
/* link style */
a {
text-decoration: none;
color: var(--theme-color);
&:hover {
text-decoration: underline;
}
&:visited {
color: hsl(290, 40%, 50%);
}
}

13
src/app.d.ts vendored Normal file
View file

@ -0,0 +1,13 @@
// See https://svelte.dev/docs/kit/types#app.d.ts
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
}
export {};

12
src/app.html Normal file
View file

@ -0,0 +1,12 @@
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<link rel="icon" href="https://moris.day/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

View file

@ -0,0 +1,193 @@
.markdown {
width: 100%;
overflow-wrap: anywhere;
@media (width>480px) {
font-size: 1.125em;
}
& h1, h2 {
font-weight: 600;
padding-top: 25px;
@media(width>999px){padding-top:50px; margin-top:0;}
&::before {
display: inline-block;
content: '§';
margin-right: 4px;
width: 4px;
color: #0000;
background-color: var(--theme-color);
}
}
& p {
margin: 0 0.5em;
line-height: 2em;
@media (width>480px) {
margin: 1em 2em;
}
}
& a[target="_blank"]:after {
content: '■';
font-size: 1em;
line-height: 1em;
width: 1em;
height: 1em;
display: inline-block;
mask-image: url('data:image/svg+xml;utf-8,<svg viewBox="0 -960 960 960" xmlns="http://www.w3.org/2000/svg"><path d="m293.3-240q-22 0-37.67-15.67-15.67-15.67-15.67-37.67v-373.3q0-22 15.67-37.67 15.67-15.67 37.67-15.67h186.7v53.33h-186.7v373.3h373.3v-186.7h53.33v186.7q0 22-15.67 37.67-15.67 15.67-37.67 15.67zm125.3-141.3-37.33-37.33 248-248h-96v-53.33h186.7v186.7h-53.33v-96z"/></svg>');
vertical-align: top;
}
& li {
line-height: 1.5em;
}
& img {
display: block;
margin: 1em auto;
max-width: 100%;
max-height: 60vh;
}
& code {
font-size: 1rem;
margin: 0 3px;
padding: 0 3px;
border-radius: 3px;
background-color: var(--code-color);
}
& details {
margin: 0 0.5em;
padding: 0 .5em;
border: dashed #8888 2px;
border-radius: 6px;
line-height: 2em;
@media (width>480px) {
margin: 1em 2em;
}
}
& pre {
position: relative;
margin: 1em;
padding: 1em;
box-sizing: border-box;
border-radius: 8px;
background-color: var(--code-block);
color: #d1d9e1;
overflow: auto;
scrollbar-width: thin;
scrollbar-color: #aaa #444;
@media (width<480px) {
margin: 1em 0;
}
& code {
margin: 0;
padding: 0;
border-radius: 0;
background-color: #fff0;
}
/*
issue: 擬似要素は { , , }
&::after {
content: '';
background-image: url('data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="white"><path d="m200-120c-44 0-80-35.6-80-80v-560c0-44 35.6-80 80-80h167c15-47.5 62.7-80 113-80 55.7 0 100 35 114 80h166c44 0 80 35.6 80 80v560c0 44-35.6 80-80 80zm0-80h560v-560h-80v120h-400v-120h-80zm280-560c22.8 0 40-17.2 40-40 0-22.8-17.2-40-40-40-22.8 0-40 17.2-40 40 0 22.8 17.2 40 40 40z"/></svg>');
width: 1.5em;
height: 1.5em;
position: absolute;
top: 1em;
right: 1em;
} */
}
& table {
margin: 1rem auto;
font-size: 1.2rem;
border-collapse: collapse;
& thead tr {
border-bottom: solid 1px var(--black-white);
}
& tbody tr {
border-top: solid color-mix(in srgb, var(--black-white) 20%, var(--back-color)) 1px;
/* background-color: color-mix(in srgb, var(--black-white) 10%, var(--back-color)); */
}
& td,th {
border-width: 0px;
padding: .5em;
}
}
& blockquote {
position: relative;
font-style: italic;
width: fit-content;
max-width: 100%;
margin: 1em auto;
border-top: 1px solid var(--font-color);
border-bottom: 1px solid var(--font-color);
& p::before {
content: '';
mask-image: url('data:image/svg+xml;utf-8,<svg version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg"><path d="m57.2 36.6-4.3 20.4h-20.5l3.8-17.9q3.3-16 10.4-23.5 7.2-7.5 16.8-8.6l-1.6 7.9q-11.7 3.2-15.7 21.7zm-31.8 0-4.3 20.4h-20.5l3.8-17.9q3.3-16 10.4-23.5 7.2-7.5 16.8-8.6l-1.6 7.9q-11.7 3.2-15.7 21.7z"/></svg>');
width: 1em;
height: 1em;
position: absolute;
background-color: var(--font-color);
top: -.4em;
left: -1em;
}
& p::after {
content: '';
mask-image: url('data:image/svg+xml;utf-8,<svg version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg"><path d="m6.8 27.4 4.3-20.4h20.5l-3.8 17.9q-3.3 16-10.4 23.5-7.17 7.5-16.8 8.6l1.6-7.9q11.7-3.2 15.7-21.7zm31.8 0 4.3-20.4h20.5l-3.8 17.9q-3.3 16-10.4 23.5-7.17 7.5-16.8 8.6l1.6-7.9q11.7-3.2 15.7-21.7z"/></svg>');
width: 1em;
height: 1em;
position: absolute;
background-color: var(--font-color);
bottom: -.4em;
right: -1em;
}
}
& iframe {
display: block;
margin: 30px auto;
border: none;
width: 80%;
aspect-ratio: 16/9;
border-radius: 12px;
box-shadow: 0px 6px 30px var(--frame-shadow);
max-height: 75vh;
transition: max-height .3s ease-in-out;
&.hide {
max-height: 30px;
}
@media(width<480px) {
width: 100%;
aspect-ratio: 4/3;
box-shadow: 0px 4px 16px var(--frame-shadow);
}
@media(width>1280px) {
width: 60%;
}
&[src*="youtube.com"]{
aspect-ratio: 16/9;
}
}
& video {
display: block;
margin: 30px auto;
width: 80%;
border-radius: 15px;
box-shadow: 0px 6px 30px var(--frame-shadow);
@media(width<480px) {
width: 100%;
}
@media(width>1280px) {
width: 60%;
}
}
& math[display="block"] {
margin: 1rem;
font-size: 1.5rem;
}
}

View file

@ -0,0 +1,30 @@
export default Perser;
import { remark } from 'remark'
import remarkgfm from 'remark-gfm'
import remarkBreaks from "remark-breaks"
import remarkmath from 'remark-math'
import remarkRehype from 'remark-rehype'
import rehypeslug from 'rehype-slug'
import rehypeKatex from 'rehype-katex'
import rehypeHighlight from 'rehype-highlight'
import rehypeExternalLinks from 'rehype-external-links'
import rehypeStringify from 'rehype-stringify'
function Perser(mdtext: string): string {
let md = remark()
.use(remarkgfm) // Github Markdown
.use(remarkBreaks) // 改行
.use(remarkmath) // 数式
.use(remarkRehype, {allowDangerousHtml: true})
.use(rehypeslug) // headingにidを設定
.use(rehypeKatex, {output:'mathml'}) // 数式
.use(rehypeHighlight) // Syntax highlight
.use(rehypeExternalLinks, {target:'_blank', rel:['noreferrer','noopener']}) // 外部サイトを新規タブで開く
.use(rehypeStringify, {allowDangerousHtml: true})
.processSync(mdtext)
.toString()
return md
}

View file

@ -0,0 +1,120 @@
/*
BSD 3-Clause License
Copyright (c) 2006, Ivan Sagalaev.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
Style with support for rainbow parens
*/
.hljs {
background: #474949;
color: #d1d9e1;
}
.hljs-comment,
.hljs-quote {
color: #969896;
font-style: italic;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-literal,
.hljs-type {
color: #cc99cc;
}
.hljs-number,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #f99157;
}
.hljs-string,
.hljs-doctag,
.hljs-regexp {
color: #8abeb7;
}
.hljs-title,
.hljs-name,
.hljs-section,
.hljs-built_in {
color: #b5bd68;
}
.hljs-variable,
.hljs-template-variable,
.hljs-selector-id,
.hljs-title.class_,
.hljs-class .hljs-title {
color: #ffcc66;
}
.hljs-section,
.hljs-strong {
font-weight: bold;
}
.hljs-symbol,
.hljs-bullet,
.hljs-subst,
.hljs-meta,
.hljs-link {
color: #f99157;
}
.hljs-deletion {
color: #dc322f;
}
.hljs-addition {
color: #68dc2e
}
.hljs-formula {
background: #eee8d5;
}
.hljs-attr,
.hljs-attribute {
color: #81a2be;
}
.hljs-emphasis {
font-style: italic;
}

View file

@ -0,0 +1,26 @@
export default getpost;
import { POST_DIR } from '$env/static/private';
import Parser from './MetaParser';
import fs from 'node:fs';
async function getpost(id:string) {
const md = fs.readFileSync(`${POST_DIR}/Posts/${id}.md`, 'utf8');
let fm_line = md.split('\n');
let line_no:number[] = []
fm_line.forEach((i,n)=>{
if(i=='---'){line_no.push(n)}
})
let a = line_no[0]
let md_text = fm_line.slice(line_no[1]+1).join('\n');
let fm_text = fm_line.slice(line_no[0]+1, line_no[1]).join('\n');
let metadata = Parser(fm_text);
return {metadata: metadata, post: md_text}
}

View file

@ -0,0 +1,34 @@
export default parser;
import yaml from "yaml"
import { parse } from '@twemoji/parser';
import { error } from '@sveltejs/kit';
function parser(fm_text:string) {
let persed = yaml.parse(fm_text);
let fms = (fm:unknown) => {
if (typeof fm === 'object' && fm !== null){
let title = 'title' in fm && typeof fm.title === 'string' ? fm.title : ''
let description = 'description' in fm && typeof fm.description === 'string'? fm.description : ''
let thumbnail = 'thumbnail' in fm && typeof fm.thumbnail === 'string' ? fm.thumbnail : ''
let emoji = 'emoji' in fm && typeof fm.emoji === 'string' ? fm.emoji : ''
let date_str = 'date' in fm && typeof fm.date === 'string' ? fm.date : ''
let category = 'category' in fm && typeof fm.category === 'string' ? fm.category : 'other'
let tags = 'tags' in fm && Array.isArray(fm.tags) ? fm.tags : []
let index = 'index' in fm && typeof fm.index === 'boolean' ? fm.index : true
let published = 'published' in fm && typeof fm.published === 'boolean' ? fm.published : false
emoji = emoji? parse(emoji)[0].url:''
let date:Date = new Date(date_str)
return {title, description,thumbnail,emoji,date,category,tags,index,published}
} else {
error(500,{message:'Invalid frontmatter'})
}
}
return fms(persed)
}

View file

@ -0,0 +1,68 @@
export default Metadatas;
import fs from 'node:fs';
import path from 'node:path';
import Posts from './PostList';
import Load from './LoadPost';
import { dev } from '$app/environment';
async function Metadatas() {
const cache_dir = '/tmp/day.moris.blog/';
const cache_file = `${cache_dir}metadata.json`;
async function build() {
const posts = await Posts();
const metadataList = await Promise.all(posts.map(async (file)=>{
const postId = path.basename(file, '.md')
const metadata = (await Load(postId)).metadata
return {postId, metadata}
}))
const sorted = metadataList
.sort((a,b)=>{
return b.metadata.date.getTime() - a.metadata.date.getTime()
})
.filter((m)=> dev||m.metadata.published)
return sorted
}
async function makeCache() {
let data = await build()
let cache_data = {
cached_time: Date.now(),
dev,
data
}
if (!fs.existsSync(cache_dir)) {
fs.mkdirSync(cache_dir), {recursive: true}
}
fs.writeFileSync(cache_file, JSON.stringify(cache_data, null, 2), 'utf8')
return data
}
if (fs.existsSync(cache_file)) {
const cache = JSON.parse(fs.readFileSync(cache_file, 'utf8'), (key,value)=>{
if(key=='date'){
return (new Date(value));
}
return value
})
if (Boolean(cache.dev)!=dev || Date.now()-cache.cached_time>5*60000) {
const data = await makeCache()
return data
} else {
return cache.data
}
} else {
const data = await makeCache()
return data
}
}

View file

@ -0,0 +1,15 @@
export default postList;
import fs from 'node:fs';
import Path from 'node:path';
import { POST_DIR } from '$env/static/private';
async function postList() {
const files = fs.readdirSync(`${POST_DIR}/Posts`);
const posts = files.filter((content)=> Path.extname(content)=='.md' && content!='README.md')
return posts
}

View file

@ -0,0 +1,121 @@
<script lang="ts">
import "../../app.css";
import Header from "./header.svelte";
import Footer from "./footer.svelte";
import { onMount } from 'svelte';
import { dev } from '$app/environment'
let { children } = $props();
let scrollObserver = $state(true);
onMount(()=>{
let observer = new IntersectionObserver(
(entries)=>{scrollObserver = entries[0].isIntersecting},
{rootMargin: "25px",threshold: 0}
);
observer.observe(document.getElementById('scroll')!)
});
</script>
<svelte:head>
<meta name="theme-color" content="#a1cc66" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="teal" media="(prefers-color-scheme: dark)">
</svelte:head>
<div id="root">
<div id="scroll"></div>
{#if dev}
<div id='devTicker'>DEV DUILD</div>
{/if}
<div class="headerContainer">
<header class:hide={!scrollObserver}>
<Header />
</header>
</div>
<div class="mainContainer">
<main>
{@render children()}
</main>
<Footer />
</div>
<div class="u320"></div>
</div>
<style>
#root {
display: flex;
flex-direction: column;
background-color: var(--back-color);
transition: background-color 1s;
min-height: 100vh;
}
#devTicker {
position: fixed;
top: 0;
left: 0;
padding: 6px;
color: yellow;
background-color: #0008;
z-index: 10;
}
.headerContainer {
position: sticky;
top: 0;
height: 80px;
z-index: 1;
@media(width<1000px) {
height: 40px;
}
& header {
background-color: var(--theme-color);
height: 100%;
transition: height .4s, transform .4s, background-color 1s;
&.hide {
height: 40px;
@media(width<1000px) {
transform: translate(0px,-50px);
}
}
}
}
.mainContainer {
display: flex;
flex-direction: column;
flex-grow: 1;
}
main {
flex-grow: 1;
}
@media(width<320px) {
.u320 {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 2;
background: no-repeat center/25% url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAgCAAAAADC94zaAAAAiklEQVQoz62RQQ7EMAgDxxH/f3LdQ9MmNGQPq3JBwsLYRkYUZQIZv+dCFgUAQgFIKxcEoJXN0NhVG8SpD8Sd0g/1s2OlPu107cNDmz2kOGIyoeQ5Xv5KRBmN5BtUKOjaFtXrtbhEbbI+XCUqXEet/Rfitp+u6/4pHEpS/eedb5GdABwbTPzMQDVyAtNNK05J5lfeAAAAAElFTkSuQmCC);
background-color: white;
image-rendering: pixelated;
color: black;
&::after {
content: 'This site supports screen sizes larger than 320px';
}
}
}
@media (scripting: none) {
.headerContainer{height:40px !important;}
}
</style>

View file

@ -0,0 +1,11 @@
<script lang="ts">
import { goto } from "$app/navigation";
import { onMount } from "svelte";
import { base } from '$app/paths'
onMount(() => {
goto(`${base}/0/`)
})
</script>
To show posts, click <a href="/blog/0">here</a>

View file

@ -0,0 +1,11 @@
import Metas from '$lib/server/Metadatas';
export async function load({params}){
const pageNo = Number(params.slug)
const postList = await Metas()
const posts = postList.slice(pageNo*12, (pageNo+1)*12);
const lastPage = Math.ceil(postList.length/12)-1;
return {posts, pageNo, lastPage}
}

View file

@ -0,0 +1,74 @@
<script lang="ts">
const { data } = $props();
import Postgrid from './grid.svelte'
import { base } from '$app/paths';
import { PUBLIC_HOSTNAME } from '$env/static/public';
</script>
<svelte:head>
<title>記事一覧 | moris.day Blog</title>
<link rel="canonical" href="https://{PUBLIC_HOSTNAME}{base}/" />
</svelte:head>
<div class='contain'>
<div class="posts">
{#each data.posts as post}
<div class="post" class:unpublished={!post.metadata.published}>
<a style='text-decoration: none;' href="../post/{post.postId}" tabindex="0">
<Postgrid id={post.postId} {...post.metadata}></Postgrid>
</a>
</div>
{/each}
{#if data.posts.length%4}
{#each Array(4-data.posts.length%4) as i}
<div class="blank"></div>
{/each}
{/if}
</div>
<div class='paging'>
<a href={data.pageNo==0? "":`../${Number(data.pageNo)-1}`} style:visibility={data.pageNo==0? 'hidden':''}></a>
<span>{data.pageNo}</span><span>/</span><span>{data.lastPage}</span>
<a href={data.pageNo==data.lastPage? "":`../${Number(data.pageNo)+1}`} style:visibility={data.pageNo==data.lastPage? 'hidden':''}></a>
</div>
</div>
<style>
.contain {
margin: 3vw;
}
.posts {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
grid-template-rows: max-content;
gap: 25px;
justify-content: center;
margin: 0 auto;
max-width: 1599px;
}
.post {
border-radius: 8px;
overflow: hidden;
box-shadow: 0 0 6px #0001;
&.unpublished {
border: solid red 3px;
}
}
.paging {
display: flex;
justify-content: center;
column-gap: 8px;
margin: 20px;
font-size: 1.5em;
font-weight: 550;
& a {
color: var(--theme-color);
}
}
</style>

View file

@ -0,0 +1,117 @@
<script lang="ts">
export let title: string;
export let description: string;
export let thumbnail: string;
export let emoji: string;
export let date: Date;
export let category: string;
export let id: string;
let twemoji = async (url: string)=>{
let res = await (await fetch(url)).text()
return res
}
</script>
<div class='grid'>
<div class='thumbnail'>
{#if thumbnail }
<img src="{thumbnail}" alt='thumbnail' style='view-transition-name: {id}'/>
{:else if emoji}
{#await twemoji(emoji)}
<div></div>
{:then emojisvg}
<div class="emoji">
{@html emojisvg}
</div>
{/await}
<!-- <div class='emoji'><img class='svg' src="{emoji}" alt="thumbnail" style='view-transition-name: {id}'/></div> -->
{:else}
<img src='data:image/svg+xml,{encodeURIComponent('<svg fill="#aaa" version="1.1" viewBox="0 -960 96 96" xmlns="http://www.w3.org/2000/svg"><path d="m41-903q-0.8 0-1.4-0.6t-0.6-1.4v-14q0-0.8 0.6-1.4t1.4-0.6h14q0.8 0 1.4 0.6t0.6 1.4v14q0 0.8-0.6 1.4t-1.4 0.6zm0-2h14v-14h-14zm1-2h12l-3.8-5-3 4-2.25-3zm-1 2v-14z"/></svg>')}' alt="fallback"/>
{/if}
<div class="tag date">{date.toLocaleDateString('sv-SE')}</div>
<div class="tag category">{category}</div>
</div>
<div class='label'>
<div class='title'>{title}</div>
<div class='description'>{description}</div>
</div>
</div>
<style>
.grid {
display: flex;
height: 100%;
flex-direction: column;
position: relative;
background-color: var(--grid-color);
transition: background-color 1s;
&:hover {
img {
transform: scale(1.03);
}
.title{
text-decoration-line: underline;
text-decoration-color: var(--font-color);
}
}
}
.thumbnail {
display: flex;
align-items: center;
justify-content: center;
aspect-ratio: 1/0.6;
width: 100%;
max-height: 200px;
& img {
display: block;
max-width: 100%;
max-height: 100%;
object-fit: contain;
margin: 0 auto;
transition: transform .3s ease-out;
}
& div.emoji {
height: 70%;
aspect-ratio: 1/1;
}
& .tag{
position: absolute;
top: 5px;
padding:2px;
color: var(--font-color);
background-color: var(--back-color);
border-radius: 3px;
font-size: 0.85em;
}
& .date {
right:5px;
}
& .category {
left: 5px;
}
}
.label {
flex-grow: 1;
height: 100%;
width: 100%;
color: var(--font-color);
& .title {
font-size: 1.17em;
font-weight: bold;
margin: 4px 8px;
}
& .description {
margin:8px;
font-size: .9em;
text-decoration: none;
}
}
</style>

View file

@ -0,0 +1,10 @@
import fs from 'node:fs';
import Perser from '$lib/components/Markdown'
export async function load(){
const profile_md = fs.readFileSync(`${process.cwd()}/src/routes/(DefaultStyle)/about/profile.md`, 'utf-8')
let persed = Perser(profile_md)
return {md: persed}
}

View file

@ -0,0 +1,26 @@
<script lang="ts">
const { data } = $props();
import '$lib/components/rainbow.css';
import '$lib/components/Markdown.css';
</script>
<svelte:head>
<title>About | moris.day Blog</title>
</svelte:head>
<div id="profile">
<article class='markdown'>
{@html data.md}
</article>
</div>
<style>
#profile {
margin: auto;
padding: 24px;
max-width: 720px;
}
</style>

View file

@ -0,0 +1,9 @@
## プロフィール
![morisのアイコン](https://moris.day/blog/icon_192.webp)
moris
## ライセンス
このブログのコンテンツは全てCC-BY-4.0の下で提供されます。
このブログのソースコードは[Gitea](https://git.moris.day/moris/moris-blog)で公開されており、MITライセンスの下で使用、改変、再頒布などを行うことができます。

View file

@ -0,0 +1,17 @@
import Metadatas from "$lib/server/Metadatas";
export async function load() {
let metalist = await Metadatas()
let cats: string[] = []
for (let meta of metalist){
let cat = meta.metadata.category;
if (!cats.includes(cat)) {
cats.push(cat)
}
}
return {cats}
}

View file

@ -0,0 +1,9 @@
<script lang="ts">
const { data } = $props();
</script>
{#each data.cats as cat}
<div>
<a href="{cat}">{cat}</a>
</div>
{/each}

View file

@ -0,0 +1,11 @@
import Metadatas from "$lib/server/Metadatas";
export async function load({params}) {
const cat = params.slug
const metalist = await Metadatas()
const result = metalist.filter((meta)=>meta.metadata.category == cat).map((meta)=>meta.postId)
return {posts: result}
}

View file

@ -0,0 +1,8 @@
<script lang="ts">
import { base } from '$app/paths'
const { data } = $props();
</script>
{#each data.posts as post}
<div><a href="{base}/post/{post}">{post}</a></div>
{/each}

View file

@ -0,0 +1,77 @@
<script lang="ts">
import { base } from '$app/paths'
</script>
<footer>
<div id="footercontents">
<div id="ftitle">
<svg class="title" version="1.1" viewBox="0 0 300 64" xmlns="http://www.w3.org/2000/svg" fill="white"><path d="m10 45.6v-26.3h3.8v6c1.2-2.56 2.3-4.3 3.34-5.3 2.57-2.4 5-1.5 6.2 0 1.24 1.64 1.17 3.7 1.17 5.65 0.723-2.5 2.13-4.6 4.1-6.26 1.2-0.92 3.72-1.66 5.56 0.54 0.8 0.96 1.2 2.86 1.2 5.72v20h-3.8v-17.9c0-2.8-0.137-3.7-0.7-4.26-1.26-1.27-2.9 0.6-3.5 1.46-0.92 1.24-1.86 3.3-2.8 6.2v14.6h-3.65v-18c0-2.3-0.225-3.7-0.68-4.1-2.2-2-4.5 1.84-6.4 6.85v15.2zm41.4 0.73c-3.7 0-6.7-1.3-9-3.9-2.24-2.6-3.4-5.95-3.4-10s1.1-7.31 3.4-9.9 5.2-3.9 9-3.9c3.7 0 6.7 1.3 9 3.9s3.4 5.9 3.4 9.9-1.1 7.3-3.4 10c-2.24 2.6-5.2 3.9-9 3.9zm0-3.65c2.3 0 4.2-0.9 5.53-2.73 1.4-1.8 2.1-4.32 2.1-7.5 0-3.2-0.685-5.7-2.1-7.5-1.37-1.8-3.2-2.73-5.53-2.73-2.33 0-4.2 0.9-5.6 2.73-1.37 1.8-2.1 4.3-2.1 7.5 0 3.2 0.69 5.7 2.1 7.5 1.37 1.8 3.2 2.7 5.6 2.7zm20 2.9v-26.3h4.35v5.74c2.44-4 5.8-6.5 10.5-6.5 1.6 0 3.15 0.243 4.7 0.73v9.5h-4.2v-6c-5.3-1-8.7 3.5-11 7.53v15.3zm34 0v-22.6h-7.24v-3.65h11.6v22.6h7.27v3.65zm-0.724-36c0-1 0.34-1.73 1-2.2s1.3-0.73 1.9-0.73c0.6 0 1.24 0.24 1.92 0.73 0.67 0.49 1 1.2 1 2.2 0 0.97-0.335 1.7-1 2.2-0.67 0.49-1.3 0.73-1.9 0.73-0.6 0-1.22-0.24-1.9-0.73-0.675-0.49-1-1.2-1-2.2zm22.4 34.4v-4.6c4.5 2.9 9.3 3.1 10.5 3.1 2.35 0 6.2-0.5 6.2-3.74-0.22-2.7-3.4-3.4-5.5-4.3l-3-1.2c-3.24-1.3-5.3-2.45-6.1-3.5-1.75-2.2-2.1-6.5 1.2-9.24 3.26-2.7 10.8-2.6 16-0.9v4c-2.9-1.04-5.6-1.55-8.1-1.55-0.5 0-5.9 0.078-5.9 3.4 0 1.5 0.81 2.4 5.7 4.2l2.94 1.1c2.7 1 4.6 2.1 5.7 3.2 2.3 2.4 2.44 7.1-0.98 10-3 2.5-10.4 3.34-18.6 0.07zm39.1 2.5c-1 0-1.94-0.38-2.7-1.13-0.75-0.75-1.12-1.65-1.1-2.7 0-1.1 0.37-2 1.12-2.7 0.76-0.75 1.66-1.13 2.7-1.13s1.92 0.376 2.66 1.13c0.763 0.737 1.14 1.64 1.14 2.7 0 1.05-0.373 1.95-1.12 2.7s-1.64 1.13-2.7 1.13zm35.5-7c-3.63 6-10.7 9.3-15.8 3.8-3.37-3.7-3.3-11.8-0.864-16.8 2.9-6.1 7.53-7.76 11.8-7.76 1.63 0.016 3.25 0.215 4.86 0.376v-11.7h4.37v38h-4.37zm0-5.2v-11.3c-1.54-0.41-3-0.61-4.3-0.61-4.67 0-9.37 3.6-9.4 11.6 0 7.36 4 7.9 4.8 7.9 2.5 0 6-2.6 8.9-7.65zm28.4 5.2c-3.5 5.8-8.4 6.6-9.9 6.6-0.7 0-8.5-0.253-8.5-11.5 0-2.65 0.58-9.68 6.36-13.8 3.55-2.6 7.93-2.14 12-1.72h4.37v18.6c0 3.17 0.327 5.74 1 7.7h-4.53c-0.343-1.44-0.615-3.4-0.82-5.9zm0-5.2v-11.3c-5.57-1.5-8.75-0.11-11 2.5-1.8 2.1-2.66 4.9-2.66 8.5 0 7.36 4 7.9 4.8 7.9 2.5 0 6-2.6 8.9-7.65zm21.2 10-12-25.2h4.8l9.6 19.7 8-19.7h4.4l-10.1 24.4c-2.14 5.16-4.16 8.6-6.1 10.3-4 3.53-9.8 2.46-11 2.2v-3.86c1.62 0.5 4.53 1 7.17-0.26 2.8-1.6 4.16-4.7 5.3-7.55z"/><line class="underline" x1="268" y1="50" x2="290" y2="50" stroke="white" stroke-width="3"/><style>.underline {animation: 1.5s step-end infinite underline;}@keyframes underline {0% {visibility: visible;}50% {visibility: hidden;}}</style></svg>
</div>
<div id='footlink'>
<dl>
<dt>Site Map</dt>
<dd><a href="https://moris.day">Top Page</a></dd>
<dd><a href="{base}/about">About me</a></dd>
<dd><a href="{base}/post">Blog</a></dd>
</dl>
<dl>
<dt>Links</dt>
<dd><a target="_blank" href="https://mi.moris.day/@moris">Misskey</a></dd>
<dd><a target="_blank" href="https://mi.moris.day/@moris">Gitea</a></dd>
<dd><a target="_blank" href="https://github.com/cocoyayann">Github</a></dd>
<dd><a target="_blank" href="https://twitter.com/cocoyayan">Twitter</a></dd>
</dl>
</div>
</div>
<div style="margin:8px auto;width:fit-content;font-size:0.8em;">
Copyright 2024 moris.
</div>
</footer>
<style>
footer {
font-family: "Source Code Pro", "Monaco", "Consolas", monospace;
height: fit-content;
background-color: var(--theme-color);
transition: background-color 1s;
color: white;
& a {
color: white;
}
& dd {
margin: 12px 0 0 24px;
}
& dt {
font-size: 1.25em;
}
& #footercontents {
display: flex;
align-items: center;
justify-content: space-around;
height: 85%;
margin: 25px;
@media(width < 720px) {
flex-direction: column;
row-gap: 20px;
}
}
& #ftitle {
margin-bottom: 20px;
}
& .title {
height: 50px;
}
& #footlink {
width:80%;
max-width: 600px;
display: flex;
justify-content: space-around;
}
}
</style>

View file

@ -0,0 +1,27 @@
<script lang="ts">
import { base } from '$app/paths'
</script>
<div class="title">
<a href="{base}/0" aria-label="blog home">
<svg version="1.1" viewBox="0 0 256 64" xmlns="http://www.w3.org/2000/svg"><path fill="white" d="m0 45.6v-26.3h3.8v6c1.2-2.56 2.3-4.3 3.34-5.3 2.57-2.4 5-1.5 6.2 0 1.24 1.64 1.17 3.7 1.17 5.65 0.723-2.5 2.13-4.6 4.1-6.26 1.2-0.92 3.72-1.66 5.56 0.54 0.8 0.96 1.2 2.86 1.2 5.72v20h-3.8v-17.9c0-2.8-0.137-3.7-0.7-4.26-1.26-1.27-2.9 0.6-3.5 1.46-0.92 1.24-1.86 3.3-2.8 6.2v14.6h-3.65v-18c0-2.3-0.225-3.7-0.68-4.1-2.2-2-4.5 1.84-6.4 6.85v15.2zm41.4 0.73c-3.7 0-6.7-1.3-9-3.9-2.24-2.6-3.4-5.95-3.4-10s1.1-7.31 3.4-9.9 5.2-3.9 9-3.9c3.7 0 6.7 1.3 9 3.9s3.4 5.9 3.4 9.9-1.1 7.3-3.4 10c-2.24 2.6-5.2 3.9-9 3.9zm0-3.65c2.3 0 4.2-0.9 5.53-2.73 1.4-1.8 2.1-4.32 2.1-7.5 0-3.2-0.685-5.7-2.1-7.5-1.37-1.8-3.2-2.73-5.53-2.73-2.33 0-4.2 0.9-5.6 2.73-1.37 1.8-2.1 4.3-2.1 7.5 0 3.2 0.69 5.7 2.1 7.5 1.37 1.8 3.2 2.7 5.6 2.7zm20 2.9v-26.3h4.35v5.74c2.44-4 5.8-6.5 10.5-6.5 1.6 0 3.15 0.243 4.7 0.73v9.5h-4.2v-6c-5.3-1-8.7 3.5-11 7.53v15.3zm34 0v-22.6h-7.24v-3.65h11.6v22.6h7.27v3.65zm-0.724-36c0-1 0.34-1.73 1-2.2s1.3-0.73 1.9-0.73c0.6 0 1.24 0.24 1.92 0.73 0.67 0.49 1 1.2 1 2.2 0 0.97-0.335 1.7-1 2.2-0.67 0.49-1.3 0.73-1.9 0.73-0.6 0-1.22-0.24-1.9-0.73-0.675-0.49-1-1.2-1-2.2zm22.4 34.4v-4.6c4.5 2.9 9.3 3.1 10.5 3.1 2.35 0 6.2-0.5 6.2-3.74-0.22-2.7-3.4-3.4-5.5-4.3l-3-1.2c-3.24-1.3-5.3-2.45-6.1-3.5-1.75-2.2-2.1-6.5 1.2-9.24 3.26-2.7 10.8-2.6 16-0.9v4c-2.9-1.04-5.6-1.55-8.1-1.55-0.5 0-5.9 0.078-5.9 3.4 0 1.5 0.81 2.4 5.7 4.2l2.94 1.1c2.7 1 4.6 2.1 5.7 3.2 2.3 2.4 2.44 7.1-0.98 10-3 2.5-10.4 3.34-18.6 0.07zm39.1 2.5c-1 0-1.94-0.38-2.7-1.13-0.75-0.75-1.12-1.65-1.1-2.7 0-1.1 0.37-2 1.12-2.7 0.76-0.75 1.66-1.13 2.7-1.13s1.92 0.376 2.66 1.13c0.763 0.737 1.14 1.64 1.14 2.7 0 1.05-0.373 1.95-1.12 2.7s-1.64 1.13-2.7 1.13zm35.5-7c-3.63 6-10.7 9.3-15.8 3.8-3.37-3.7-3.3-11.8-0.864-16.8 2.9-6.1 7.53-7.76 11.8-7.76 1.63 0.016 3.25 0.215 4.86 0.376v-11.7h4.37v38h-4.37zm0-5.2v-11.3c-1.54-0.41-3-0.61-4.3-0.61-4.67 0-9.37 3.6-9.4 11.6 0 7.36 4 7.9 4.8 7.9 2.5 0 6-2.6 8.9-7.65zm28.4 5.2c-3.5 5.8-8.4 6.6-9.9 6.6-0.7 0-8.5-0.253-8.5-11.5 0-2.65 0.58-9.68 6.36-13.8 3.55-2.6 7.93-2.14 12-1.72h4.37v18.6c0 3.17 0.327 5.74 1 7.7h-4.53c-0.343-1.44-0.615-3.4-0.82-5.9zm0-5.2v-11.3c-5.57-1.5-8.75-0.11-11 2.5-1.8 2.1-2.66 4.9-2.66 8.5 0 7.36 4 7.9 4.8 7.9 2.5 0 6-2.6 8.9-7.65zm21.2 10-12-25.2h4.8l9.6 19.7 8-19.7h4.4l-10.1 24.4c-2.14 5.16-4.16 8.6-6.1 10.3-4 3.53-9.8 2.46-11 2.2v-3.86c1.62 0.5 4.53 1 7.17-0.26 2.8-1.6 4.16-4.7 5.3-7.55z"/></svg>
</a>
</div>
<style>
.title {
display: flex;
height: 100%;
align-items: center;
justify-content: center;
a {
display: block;
height: 75%;
}
svg {
height: 100%
}
}
</style>

View file

@ -0,0 +1,9 @@
import Metas from '$lib/server/Metadatas';
export async function load(){
const postList = await Metas()
let id = postList[0].postId
return {id}
}

View file

@ -0,0 +1,15 @@
<script lang="ts">
const { data } = $props();
import { goto } from "$app/navigation";
import { onMount } from "svelte";
import { base } from '$app/paths'
onMount(() => {
goto(`${base}/post/${data.id}`)
})
</script>
<svelte:head>
<meta http-equiv="refresh" content="0; URL={data.id}" />
</svelte:head>

View file

@ -0,0 +1,28 @@
import Post from '$lib/server/LoadPost';
import { remark } from 'remark'
import Perser from '$lib/components/Markdown'
import GithubSlugger from 'github-slugger';
export async function load({params}) {
const md = await Post(params.slug);
let persed = Perser(md.post)
let mdast = remark().parse(md.post)
let headers = mdast.children.filter((i) => i.type=='heading').map((i)=>{
let title = i.children[0].value
return {"depth": i.depth, "title": title}
})
const slugs = new GithubSlugger()
let slugged = headers.map((x)=>x.depth ?slugs.slug(x.title):'').filter((x)=>x!='')
return {
id: params.slug,
metadata: md.metadata,
post: persed,
heading: slugged,
}
}

View file

@ -0,0 +1,311 @@
<script lang="ts">
const { data } = $props();
import Toc from './toc.svelte'
import Share from './share.svelte'
import Profile from './profile.svelte'
import { base } from '$app/paths';
import { PUBLIC_HOSTNAME, PUBLIC_POST_REPO } from '$env/static/public';
import { onMount } from 'svelte';
import '$lib/components/rainbow.css';
import '$lib/components/Markdown.css';
const baseURL = `https://${PUBLIC_HOSTNAME}${base}`
onMount(()=>{
let toggleHide = function(e:MessageEvent){
if (e.data.message === 'toggleHide') {
let iframes = document.querySelectorAll('.markdown iframe')
for ( let i = 0; i < iframes.length; i++ ){
let frame = iframes[i]
if (frame instanceof HTMLIFrameElement && new URL(frame.src).origin === window.origin && frame.contentWindow?.name == e.data.id){
iframes[i].classList.toggle('hide')
}
}
}
}
window.addEventListener('message', toggleHide)
document.getElementById('blog')?.addEventListener('click',(e:MouseEvent)=>{
if (e.target instanceof HTMLElement && e.target.matches('pre')) {
let code = e.target.textContent || '';
navigator.clipboard.writeText(code)
}
})
// リスナ解除
return ()=>{window.removeEventListener('message', toggleHide)}
})
</script>
<svelte:head>
<title>{data.metadata.title} | moris.day Blog</title>
<meta property="og:type" content="article">
<meta property="og:title" content="{data.metadata.title}">
<meta property="og:url" content="{baseURL}/post/{data.id}">
<meta property="og:description" content="{data.metadata.description}">
{#if data.metadata.thumbnail}
<meta property="og:image" content="{data.metadata.thumbnail}">
{/if}
{#if !data.metadata.index}
<meta name="robots" content="noindex">
{/if}
</svelte:head>
<div id='blog'>
<article>
<h1 class="title">{data.metadata.title}</h1>
<div class="meta">
<div class="category">
<span class="txt">Category:</span>
<a class="tag" href='/blog/category/{data.metadata.category}'>{data.metadata.category}</a>
</div>
{#if data.metadata.tags.length }
<div class="divider"></div>
<div class="tags">
<span class="txt">Tags:</span>
{#each data.metadata.tags as tag}
<a class='tag' href='/blog/tag/{tag}'>{tag}</a>
{/each}
</div>
{/if}
<div class="date">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M480-120q-138 0-240.5-91.5T122-440h82q14 104 92.5 172T480-200q117 0 198.5-81.5T760-480q0-117-81.5-198.5T480-760q-69 0-129 32t-101 88h110v80H120v-240h80v94q51-64 124.5-99T480-840q75 0 140.5 28.5t114 77q48.5 48.5 77 114T840-480q0 75-28.5 140.5t-77 114q-48.5 48.5-114 77T480-120Zm112-192L440-464v-216h80v184l128 128-56 56Z"/></svg>
<span>{data.metadata.date.toLocaleDateString('sv-SE')}</span>
</div>
</div>
{#if data.metadata.thumbnail}
<img class="thumbnail" alt="thumbnail" src="{data.metadata.thumbnail}" style='view-transition-name: {data.id}'>
{:else if data.metadata.emoji}
<div class='thumbnail emoji'><img class="emoji" alt="thumbnail" src="{data.metadata.emoji}" style='view-transition-name: {data.id}'></div>
{/if}
<div class='markdown'>
{@html data.post}
</div>
<div class="data">
{#if PUBLIC_POST_REPO}
<div><a target="_blank" href="{PUBLIC_POST_REPO}/commits/branch/main/Posts/{data.id}.md">History</a></div>
<div class="spacer"></div>
{/if}
<div><a target="_blank" href="raw.md">Raw file</a></div>
</div>
</article>
<aside>
<div id='side'>
<div>
<Profile></Profile>
</div>
<div>
<Share share={{url:`${baseURL}/post/${data.id}`, title:data.metadata.title}} />
</div>
{#if data.heading.length!=0}
<div id='toc'>
<Toc toclist={data.heading}></Toc>
</div>
{/if}
<div class="mi-posts js">
<!-- <iframe src="https://mi.moris.day/embed/user-timeline/9w7bhjzt2b5z0001?maxHeight=320&rounded=false&border=false" title="moris's posts" loading="lazy" style="border:none; width:100%; height:100%;"></iframe> -->
</div>
</div>
</aside>
</div>
<style>
#blog {
display: flex;
margin: 25px 2.5%;
gap: 20px;
}
aside {
min-width: 280px;
max-width: 320px;
@media (width<1000px) {
display: none;
}
& #side {
display: flex;
flex-direction: column;
row-gap: 20px;
position: sticky;
top: 70px;
}
& #side>* {
background-color: var(--grid-color);
transition: background-color 1s;
border-radius: 8px;
box-shadow: 0 0 6px #1111;
overflow: hidden;
}
& .mi-posts {
height: 320px;
@media(prefers-color-scheme: dark) {
border: solid #444 1px;
}
}
}
article {
min-width: 0;
max-width: 60rem;
margin: 0 auto;
flex-grow: 1;
padding: 0 20px;
border-radius: 8px;
@media(width<480px){
padding: 0;
}
& .title {
font-size: 2em;
font-weight: 600;
margin: 0;
padding: 5px;
border-bottom: 1px solid;
@media(width<480px) {
font-size: 1.5em;
}
}
& .thumbnail {
display: block;
margin: 0 auto;
max-height: 50vh;
}
& img.thumbnail {
height: 35vh;
max-width: 100%;
object-fit: contain;
}
& .emoji.thumbnail {
width: 30%;
max-width: 30vh;
aspect-ratio: 1;
@media(width<480px) {
width: 50%;
}
& img {
margin: 15%;
}
}
& .markdown {
margin: 25px 0;
@media (width<480px) {
margin: 8px;
}
}
& .data {
display: flex;
padding: 8px;
border-top: solid 1px;
font-weight: 200;
font-size: 1.2em;
& .spacer {
width: 1px;
background-color: var(--font-color);
box-sizing: border-box;
margin: 2px 6px;
}
& a {
margin: 4px;
color: var(--font-color);
text-decoration-thickness: 1px;
}
}
}
.meta {
display: grid;
grid-template-rows: auto auto;
grid-template-columns: auto auto 1fr auto;
gap: 8px;
font-size: 0.95em;
margin: 8px;
white-space: nowrap;
@media(width<1000px) {
flex-direction: column;
}
& .category {
grid-row: 1;
grid-column: 1;
}
& .tags {
grid-row: 1;
grid-column: 3;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@media(width<1000px) {
grid-row: 2;
grid-column: 1 / 5;
}
@media(width<480px){
display: inline-block;
grid-row: 1;
grid-column: 3;
}
}
& .tag {
display: inline-block;
background-color: var(--theme-color);
color: var(--font-color);
border-radius: 3px;
padding: 0 4px 1px 4px;
margin: 3px;
}
& .divider {
display: inline-block;
width: 1px;
margin: 3px 0;
background-color: var(--font-color);
grid-row: 1;
grid-column: 2;
@media(480px<width<1000px){
display: none;
}
}
& .date{
display: flex;
grid-row: 1;
grid-column: 4;
vertical-align: middle;
align-items: center;
@media(width<480px){
display: inline-block;
grid-row: 2;
grid-column: 1 / 5;
}
}
& .date svg{
height: 1rem;
fill: var(--font-color);
vertical-align: middle;
}
@media(width<480px) {
& .txt {display: none;}
}
}
</style>

View file

@ -0,0 +1,49 @@
<div class="profile" aria-label="プロフィール">
<div>
<div class="space topspace"></div>
<div class="icon">
<img src="https://moris.day/files/assets/moris_icon_200px.avif" width="200" height="200" alt="moris icon">
</div>
<div class="space bottomspace"></div>
<div style="position:relative; text-align:center; font-family:monospace;font-size:1.2em; font-weight:350; width:fit-content; margin:auto;"><span>moris</span><img src="https://cdn.jsdelivr.net/gh/jdecked/twemoji@latest/assets/svg/1f9ea.svg" alt="test tube" style="position:absolute; left:100%; aspect-ratio:1; height:100%; padding:3%; box-sizing:border-box;"></div>
</div>
<div class="discription">
<span>有機化学好きの大学生<br>電子工作やプログラムもいじります<br>誤字 文句 その他連絡→<a href="https://mi.moris.day/@moris" target="_blank">Misskey(Fediverse)</a></span>
</div>
</div>
<style>
.profile {
position: relative;
}
.topspace {
height: 55px;
background-color: color-mix(in srgb, var(--theme-color) 25%, var(--back-color));
}
.bottomspace {
height: 38px;
}
.icon {
position: absolute;
padding: 4px;
border-radius: 50%;
width: 70px;
height: 70px;
top: 14px; /*calc(55px - 35px - 6px);*/
left: calc(50% - 41px); /*calc(50% - 35px - 6px);*/
background-color: var(--grid-color);
& img {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.discription {
padding: 8px;
font-size: 0.9em;
text-align: center;
line-height: 1.8;
}
</style>

View file

@ -0,0 +1,9 @@
export const prerender = true
import fs from 'node:fs';
import { POST_DIR } from '$env/static/private';
export async function GET({params}) {
const raw = fs.readFileSync(`${POST_DIR}/Posts/${params.slug}.md`,{encoding:'utf-8'})
return new Response(raw);
};

View file

@ -0,0 +1,92 @@
<script lang="ts">
type shareobj = {
url: string,
title: string
}
export let share: shareobj;
import { base } from '$app/paths'
let copyed = false;
function copylink() {
navigator.clipboard.writeText(share.url);
copyed = true;
setTimeout(() => (copyed = false), 1000);
}
</script>
<div style="margin:4px 0 0 8px;color:#aaaa;font-size:0.85em">Share</div>
<div id="share">
<button class="link js" type="button" on:click={copylink} aria-label="urlをコピー" title="copy url">
{#if copyed}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="m620-163-170-170 56-56 114 114 226-226 56 56zm220-397h-80v-200h-80v120h-400v-120h-80v560h240v80h-240c-44 0-80-35.6-80-80v-560c0-44 35.6-80 80-80h167c15-47.5 62.7-80 113-80 55.7 0 99.9 35 114 80h166c44 0 80 35.6 80 80zm-360-200c22.8 0 40-17.2 40-40 0-22.8-17.2-40-40-40-22.8 0-40 17.2-40 40 0 22.8 17.2 40 40 40z"/></svg>
{:else}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="m200-120c-44 0-80-35.6-80-80v-560c0-44 35.6-80 80-80h167c15-47.5 62.7-80 113-80 55.7 0 100 35 114 80h166c44 0 80 35.6 80 80v560c0 44-35.6 80-80 80zm0-80h560v-560h-80v120h-400v-120h-80zm280-560c22.8 0 40-17.2 40-40 0-22.8-17.2-40-40-40-22.8 0-40 17.2-40 40 0 22.8 17.2 40 40 40z"/></svg>
{/if}
</button>
<div class="misskey">
<a href="https://misskey-hub.net/share?url={share.url}&text={share.title}+-+moris+Blog" target="_blank" aria-label="misskeyで共有">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 136 136"><path d="m16.5 20c-9 0-16.5 7.5-16.5 16.5v62c0 9 7.5 16.5 16.5 16.5 9 0 16.5-7.5 16.5-16.5v-12c0-3 2.6-2 4 0 2.4 4 7.4 7.6 13.3 7.5s10.9-3.5 13.3-7.5c1.4-2 4-3 4 0v12c0 9 7.5 16.5 16.5 16.5 9 0 16.5-9 16.5-16.5v-62c0-9-7.5-16.5-16.5-16.5-5 0-9.4 2-13 6l-16.5 19.5c-2.45 3.13-5.96 2.92-8.4 0l-16.5-19.5c-3.4-4-7.7-6-13-6zm105.5 0c-8 0-14 6-14 14 0 8 6 14 14 14 8 0 14-6 14-14 0-8-6-14-14-14m0 31c-8 0-14 6 -14 14v35c0 8 6 14 14 14 8 0 14-6 14-14v-35c0-8-6-14-14-14"/></svg>
</a>
</div>
<div class="threads">
<a href="https://www.threads.net/intent/post?text={share.title}+-+moris+Blog%0A{share.url}" target="_blank" aria-label="threadsで共有">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1100 1100"><path d="m558 1050c-229 0-444-129-446-499v-0.7c1.4-172 56-496 446-499h0.6c231 1.6 377 122 430 315l-85 23.7c-46-165-162-249-346-251-284 2.1-356 208-358 411 2.8 325 179 410 358 411 109-0.8 182-26 242-85 83.6-82 81-212-22.7-273-34.2 240-253 237-344 177-92-60-97-184-17.6-252 82-71 221-57 275-48-25-150-195-129-244-55l-73-49c93-138 393-152 409 130 196 83 203 303 80 432-82 88-172 112-304 112zm11-486c-173 9.7-151 171-6 163 51-2.7 117-22.6 129-155-41-3.7-82-10-123-8z"/></svg>
</a>
</div>
<div class="twitter">
<a href="https://twitter.com/share?url={share.url}&text={share.title}+-+moris+Blog%0A" target="_blank" aria-label="twitterで共有">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 248 248"><path d="M 222,73 C 222,205 87,257 1,201 28,204 54,196 76,180 54,180 35,166 29,145 37,147 44,146 52,144 28,139 11,118 11,94 18,98 26,100 34,100 12,85 5,56 18,33 44,65 81,84 122,86 111,38 172,2 208,40 219,38 230,34 240,28 236,40 228,50 218,56 227,56 238,52 247,48Z"/></svg>
</a>
</div>
<div class="rss">
<a href="{base}/feed.rss" target="_blank" aria-label="rss配信">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M200-120q-33 0-56.5-23.5T120-200q0-33 23.5-56.5T200-280q33 0 56.5 23.5T280-200q0 33-23.5 56.5T200-120Zm480 0q0-117-44-218.5T516-516q-76-76-177.5-120T120-680v-120q142 0 265 53t216 146q93 93 146 216t53 265H680Zm-240 0q0-67-25-124.5T346-346q-44-44-101.5-69T120-440v-120q92 0 171.5 34.5T431-431q60 60 94.5 139.5T560-120H440Z"/></svg>
</a>
</div>
</div>
<style>
#share {
display: flex;
gap: 4px;
height: 36px;
padding: 5px;
& > * {
height: 100%;
flex: 1;
border-radius: 3px;
&:hover {
background-color: #0001;
}
}
}
button {
all: unset;
}
svg {
height: 100%;
width: 100%;
fill: #aaaa;
transition: fill 0.2s;
}
.link svg:hover {
fill: hsl(0, 25%, 30%);
}
.misskey svg:hover {
fill: hsl(75, 99%, 40%);
}
.threads svg:hover {
fill: black;
}
.twitter svg:hover {
fill: hsl(203, 100%, 65%);
}
.rss svg:hover {
fill: orange;
}
</style>

View file

@ -0,0 +1,46 @@
<script lang="ts">
export let toclist: string[];
</script>
<nav>
<div style="margin:4px 0 0 8px;color:#aaaa;font-size:0.85em;">目次</div>
<ul>
{#each toclist as toc}
<li><a href='#{toc}'>{toc}</a></li>
{/each}
</ul>
</nav>
<style>
ul {
margin: 0;
padding: 5px;
font-size: .9rem;
max-height: 180px;
overflow: auto;
scrollbar-width: thin;
scrollbar-color: #aaa #444;
}
li {
padding: 4px;
border-radius: 3px;
}
li:hover {
background-color: #0002;
}
a {
display: block;
text-decoration: none;
color: var(--font-color);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&::before {
content: '・';
color: var(--font-color);
}
}
</style>

View file

@ -0,0 +1,17 @@
import Metadatas from "$lib/server/Metadatas";
export async function load() {
let metalist = await Metadatas()
let tags: string[] = []
for (let meta of metalist){
for (let tag of meta.metadata.tags) {
if (!tags.includes(tag)) {
tags.push(tag)
}
}
}
return {tags}
}

View file

@ -0,0 +1,9 @@
<script lang="ts">
const { data } = $props();
</script>
{#each data.tags as tag}
<div>
<a href="{tag}">{tag}</a>
</div>
{/each}

View file

@ -0,0 +1,11 @@
import Metadatas from "$lib/server/Metadatas";
export async function load({params}) {
const tag = params.slug
const metalist = await Metadatas()
const result = metalist.filter((meta)=>meta.metadata.tags.includes(tag)).map((meta)=>meta.postId)
return {posts: result}
}

View file

@ -0,0 +1,8 @@
<script lang="ts">
import { base } from '$app/paths'
const { data } = $props();
</script>
{#each data.posts as post}
<div><a href="{base}/post/{post}">{post}</a></div>
{/each}

2
src/routes/+layout.ts Normal file
View file

@ -0,0 +1,2 @@
export const prerender = true;
export const trailingSlash = 'always';

View file

@ -0,0 +1 @@
export const csr = false

View file

@ -0,0 +1,12 @@
import { POST_DIR } from '$env/static/private';
import Path from 'node:path';
import fs from 'node:fs';
export async function load({}) {
const files = fs.readdirSync(`${POST_DIR}/Codes`);
const codes = files.filter((content)=> Path.extname(content)=='.html')
return {codes}
}

View file

@ -0,0 +1,10 @@
<script lang="ts">
const { data } = $props();
import { base } from '$app/paths';
</script>
<ul>
{#each data.codes as code}
<li><a href="{base}/code/{code}">{code}</a></li>
{/each}
</ul>

View file

@ -0,0 +1,75 @@
<script lang="ts">
let { children } = $props();
import './app.css'
function toggleFullScreen() {
if (document.fullscreenElement) {
document.exitFullscreen();
} else {
document.querySelector("body")?.requestFullscreen();
}
}
function hideContent() {
let uuid = crypto.randomUUID()
window.name = uuid
window.parent.postMessage({"id": uuid, 'message': 'toggleHide'}, '*');
}
</script>
<svelte:head>
<meta name="robots" content="noindex">
</svelte:head>
<div id='window'>
<div class="menubar">
<button style="background-color: rgb(255 95 87)" aria-label="reload" onclick={()=>{location.reload()}}></button>
<button style="background-color: rgb(255 188 46)" aria-label="close" onclick={hideContent}></button>
<button style="background-color: rgb(40 200 64)" aria-label="fullscreen" onclick={toggleFullScreen}></button>
</div>
<div class="code">
{@render children()}
</div>
</div>
<style>
#window {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
box-sizing: border-box;
border: solid var(--border-color) 1px;
border-radius: 12px;
box-shadow: 0px 8px 40px var(--shadow-color);
background-color: var(--back-color);
font-family: sans-serif;
overflow: hidden;
}
.menubar {
display: flex;
height: 28px;
padding: 0 4px;
border-radius: 12px 12px 0 0;
background-color: var(--head-color);
border-bottom: solid var(--divider-color) 1px;
& >* {
all: unset;
width: 12px;
height: 12px;
margin: 8px 4px;
border-radius: 50%;
background-color: gray;
cursor: pointer;
}
}
.code {
height: calc(100% - 28px);
overflow-x: hidden;
overflow-y: auto;
}
</style>

View file

@ -0,0 +1,10 @@
import { POST_DIR } from '$env/static/private';
import fs from 'node:fs';
export async function load({params}) {
const code = params.slug.replace(/\.html$/, "");
const html = fs.readFileSync(`${POST_DIR}/Codes/${code}.html`, 'utf-8');
return {html}
}

View file

@ -0,0 +1,5 @@
<script lang="ts">
const { data } = $props();
</script>
<div>{@html data.html}</div>

View file

@ -0,0 +1,35 @@
:root {
height: 100%;
background-color: var(--white-black);
--back-color: white; /*rgb(240 238 245)*/
--head-color: rgb(245, 245, 245); /*rgb(244 243 250)*/
--divider-color: rgb(220 220 220);
--border-color: #bbb;
--shadow-color: #0009;
--white-black: #fff;
--font-color: #222;
@media(prefers-color-scheme: dark){
--back-color: rgb(43 48 55);
--head-color: rgb(58 60 66);
--divider-color: #222;
--border-color: #555;
--shadow-color: #000;
--white-black: #000;
--font-color: #f5f5f5;
}
}
body {
margin: 0;
height: 100%;
background-color: var(--white-black);
color: var(--font-color);
}
body:fullscreen {
padding: 10%;
background-image: url(https://moris.day/files/img/BigSur.avif);
background-size: cover;
}

View file

@ -0,0 +1,46 @@
export const prerender = true
import path from 'node:path';
import Metadatas from '$lib/server/Metadatas';
import { PUBLIC_HOSTNAME } from '$env/static/public';
/** @type {import('./$types').RequestHandler} */
export async function GET() {
let items = ""
let i = 0
const metalist = await Metadatas()
for (let post of metalist){
let meta = post.metadata
let link = `https://${PUBLIC_HOSTNAME}/blog/post/${path.basename(post.postId, '.md')}`
i++
items += `
<item>
<title>${meta.title}</title>
<description>${meta.description}</description>
<link>${link}</link>
<pubDate>${meta.date.toUTCString()}</pubDate>
<guid>${link}</guid>
</item>`
if (i==15) {break}
}
let rss = `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>moris.day blog RSSfeed</title>
<description></description>
<link>https://moris.day/blog</link>
<pubDate>${(new Date()).toUTCString()}</pubDate>
${items}
</channel>
</rss>`
return new Response(rss);
};

17
svelte.config.js Normal file
View file

@ -0,0 +1,17 @@
import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: vitePreprocess(),
kit: {
adapter: adapter(),
paths: {
base: "/blog",
relative: false
}
}
};
export default config;

19
tsconfig.json Normal file
View file

@ -0,0 +1,19 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
}
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}

9
vite.config.ts Normal file
View file

@ -0,0 +1,9 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()],
server: {
port: 5273
}
});