Rewrite it in Rust
This commit is contained in:
parent
7a36b62138
commit
dc96e49b34
28
.gitignore
vendored
28
.gitignore
vendored
|
@ -1,17 +1,5 @@
|
|||
# build output
|
||||
dist/
|
||||
# generated types
|
||||
.astro/
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
|
@ -24,15 +12,17 @@ pnpm-debug.log*
|
|||
public/**/*.jpg
|
||||
public/**/*.png
|
||||
public/**/*.webp
|
||||
src/**/*.jpg
|
||||
src/**/*.png
|
||||
src/**/*.webp
|
||||
|
||||
# obsidian
|
||||
.obsidian/
|
||||
.stfolder/
|
||||
content/**/*.jpg
|
||||
content/**/*.png
|
||||
content/**/*.webp
|
||||
|
||||
# Kanji generator
|
||||
tools/kklc/kklc.csv
|
||||
tools/kklc/target/
|
||||
public/static/kanji/
|
||||
|
||||
# Rust
|
||||
target/
|
||||
|
||||
# JavaScript
|
||||
js/**/node_modules/
|
||||
|
|
4
.vscode/extensions.json
vendored
4
.vscode/extensions.json
vendored
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"recommendations": ["astro-build.astro-vscode"],
|
||||
"unwantedRecommendations": []
|
||||
}
|
11
.vscode/launch.json
vendored
11
.vscode/launch.json
vendored
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "./node_modules/.bin/astro dev",
|
||||
"name": "Development server",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
3
.vscode/ltex.dictionary.en-US.txt
vendored
3
.vscode/ltex.dictionary.en-US.txt
vendored
|
@ -1,3 +0,0 @@
|
|||
Astro
|
||||
prerender
|
||||
KaTeX
|
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"files.associations": {
|
||||
// "*.mdx": "markdown"
|
||||
},
|
||||
"ltex.language": "en-US"
|
||||
}
|
1449
Cargo.lock
generated
Normal file
1449
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,22 +1,26 @@
|
|||
[package]
|
||||
name = "ssg"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
name = "treesitter"
|
||||
version = "0.0.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
|
||||
napi = { version = "2.12.2", default-features = false, features = ["napi4"] }
|
||||
napi-derive = "2.12.2"
|
||||
aho-corasick = "1.1.3"
|
||||
chrono = "0.4.35"
|
||||
comrak = { version = "0.22.0", default-features = false, features = ["shortcodes"] }
|
||||
glob = "0.3.1"
|
||||
grass = { version = "0.13.2", default-features = false, features = ["random"] }
|
||||
gray_matter = { version = "0.2.6", default-features = false, features = ["yaml"] }
|
||||
hayagriva = "0.5.2"
|
||||
hypertext = "0.5.0"
|
||||
katex = "0.4.6"
|
||||
once_cell = "1.19.0"
|
||||
regex = "1.10.4"
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
|
||||
# Treesitter
|
||||
tree-sitter = "0.20.10"
|
||||
tree-sitter-highlight = "0.20.1"
|
||||
|
||||
# Languages
|
||||
tree-sitter-astro = { git = "https://github.com/virchau13/tree-sitter-astro.git", rev = "e924787e12e8a03194f36a113290ac11d6dc10f3" }
|
||||
tree-sitter-css = "0.20.0"
|
||||
tree-sitter-haskell = { git = "https://github.com/tree-sitter/tree-sitter-haskell", rev = "1da347c88599faad7964e63facead5d163ac7dba" }
|
||||
|
@ -30,10 +34,3 @@ tree-sitter-rust = "0.20.4"
|
|||
tree-sitter-scheme = { git = "https://github.com/6cdh/tree-sitter-scheme", rev = "af0fd1fa452cb2562dc7b5c8a8c55551c39273b9" }
|
||||
tree-sitter-toml = "0.20.0"
|
||||
tree-sitter-typescript = "0.20.5"
|
||||
|
||||
[build-dependencies]
|
||||
napi-build = "2.0.1"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
strip = "symbols"
|
19
Makefile
19
Makefile
|
@ -1,19 +1,8 @@
|
|||
update:
|
||||
pnpm update
|
||||
pnpm update svelte@next
|
||||
|
||||
dev:
|
||||
pnpm run astro dev
|
||||
|
||||
build:
|
||||
pnpm run astro build
|
||||
cargo run
|
||||
|
||||
deploy: build
|
||||
rsync -Pavz ./dist/ kamoshi:/var/www/kamoshi.org --delete
|
||||
rsync -Pavz ./dist/ kamoshi:/var/www/kamoshi.org --delete --mkpath
|
||||
|
||||
preview: build
|
||||
pnpm run astro preview
|
||||
|
||||
treesitter:
|
||||
cd ./tools/treesitter; \
|
||||
pnpm run build
|
||||
serve:
|
||||
python -m http.server -d ./dist
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import sitemap from '@astrojs/sitemap';
|
||||
import mdx from '@astrojs/mdx';
|
||||
import svelte from '@astrojs/svelte';
|
||||
import pagefind from 'astro-pagefind';
|
||||
import remarkDirective from 'remark-directive';
|
||||
import remarkMath from 'remark-math';
|
||||
import rehypeKatex from 'rehype-katex';
|
||||
import remarkEmoji from 'remark-emoji';
|
||||
import remarkBib from './src/utils/remark/bib';
|
||||
import remarkRuby from './src/utils/remark/ruby';
|
||||
import rehypeTreesitter from './src/utils/treesitter';
|
||||
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: 'https://kamoshi.org',
|
||||
trailingSlash: 'always',
|
||||
markdown: {
|
||||
syntaxHighlight: false,
|
||||
remarkPlugins: [
|
||||
remarkDirective,
|
||||
remarkMath,
|
||||
[remarkEmoji, { accessible: true }],
|
||||
[remarkRuby, { sep: ';' }],
|
||||
remarkBib,
|
||||
],
|
||||
rehypePlugins: [
|
||||
// https://katex.org/docs/options.html
|
||||
[rehypeKatex, { output: 'mathml' }],
|
||||
rehypeTreesitter,
|
||||
],
|
||||
},
|
||||
integrations: [
|
||||
mdx(),
|
||||
svelte({ compilerOptions: { runes: true } }),
|
||||
pagefind(),
|
||||
sitemap()
|
||||
]
|
||||
});
|
|
@ -1,9 +1,7 @@
|
|||
---
|
||||
layout: ../layouts/Wiki.astro
|
||||
title: "About me"
|
||||
date: 2021-09-10T19:34:01+02:00
|
||||
---
|
||||
import Timeline from "@components/Timeline.astro";
|
||||
|
||||
I studied Computer Science at a university and I have professional experience
|
||||
as a TypeScript developer. I can use languages such as Rust, C#, TypeScript or
|
||||
|
@ -21,23 +19,6 @@ ahead of me, I am determined to achieve fluency in Japanese language in the
|
|||
future.
|
||||
|
||||
|
||||
一番好きな同人サークルは
|
||||
|
||||
- Alstroemeria Records(音楽)
|
||||
- Shibayan Records(音楽)
|
||||
- Syrufit(音楽)
|
||||
- 凋叶棕(音楽)
|
||||
- azmaya(同人誌)
|
||||
- へ蝶々(同人誌)
|
||||
|
||||
※ Syrufitさんはもう活動を停止しました。
|
||||
|
||||
|
||||
## Timeline
|
||||
|
||||
<Timeline />
|
||||
|
||||
|
||||
## Q&A
|
||||
|
||||
**Q. Why did you create this website?**
|
||||
|
@ -48,6 +29,13 @@ of me working on this. The CSS styles, for example, were all hand rolled by
|
|||
myself. To simplify everything it's a static website served from Nginx, this
|
||||
way I don't have to do nearly any maintenance work.
|
||||
|
||||
**Q. Can I take a look at the source code?**
|
||||
|
||||
Sure thing! The source code is available in
|
||||
[a repository on GitHub](https://github.com/kamoshi/kamoshi.org),
|
||||
and you're welcome to learn. I have just one request please, if you would like
|
||||
to use this code for your own website do allow others to learn from it too!
|
||||
|
||||
**Q. What's your setup?**
|
||||
|
||||
I currently use Arch Linux with KDE Plasma desktop as my daily driver. For
|
|
@ -1,3 +1,7 @@
|
|||
---
|
||||
title: "Test"
|
||||
date: 2021-09-10T19:34:01+02:00
|
||||
---
|
||||
# Welcome to my website! :heart:
|
||||
|
||||
You have found this little floating rock in the middle of the Internet! Congrats 🎉
|
Before Width: | Height: | Size: 423 KiB After Width: | Height: | Size: 423 KiB |
|
@ -7,7 +7,7 @@ animate: true
|
|||
|
||||
# Dead simple Haskell
|
||||
|
||||
![Haskell logo](/static/content/slides/haskell-molecules/haskell.png)
|
||||
![Haskell logo](haskell.png)
|
||||
|
||||
-----
|
||||
|
||||
|
@ -17,11 +17,11 @@ animate: true
|
|||
|
||||
### Atoms
|
||||
|
||||
![atoms](/static/content/slides/haskell-molecules/atoms.png)
|
||||
![atoms](atoms.png)
|
||||
|
||||
---
|
||||
|
||||
![atoms](/static/content/slides/haskell-molecules/atoms.png)
|
||||
![atoms](atoms.png)
|
||||
|
||||
```haskell
|
||||
data H = H -- hydrogen
|
||||
|
@ -33,11 +33,11 @@ data C = C -- carbon
|
|||
|
||||
### Molecules
|
||||
|
||||
![Atoms](/static/content/slides/haskell-molecules/molecules.png)
|
||||
![Atoms](molecules.png)
|
||||
|
||||
---
|
||||
|
||||
![Atoms](/static/content/slides/haskell-molecules/molecules.png)
|
||||
![Atoms](molecules.png)
|
||||
|
||||
```haskell
|
||||
type H₂O = (H, O, H) -- water
|
||||
|
@ -49,15 +49,15 @@ type CO₂ = (O, C, O) -- carbon dioxide
|
|||
|
||||
### Reactions
|
||||
|
||||
![Magic](/static/content/slides/haskell-molecules/magic.png)
|
||||
![Magic](magic.png)
|
||||
|
||||
---
|
||||
|
||||
![More magic](/static/content/slides/haskell-molecules/more-magic.png)
|
||||
![More magic](more-magic.png)
|
||||
|
||||
---
|
||||
|
||||
![More magic](/static/content/slides/haskell-molecules/more-magic.png)
|
||||
![More magic](more-magic.png)
|
||||
|
||||
```haskell ignore
|
||||
makeWater :: H -> H -> O -> H₂O
|
||||
|
@ -98,11 +98,11 @@ burnOxygen c (o1, o2) = (o1, c, o2)
|
|||
|
||||
### `.` `$`
|
||||
|
||||
![Plumbing](/static/content/slides/haskell-molecules/plumbing.png)
|
||||
![Plumbing](plumbing.png)
|
||||
|
||||
---
|
||||
|
||||
![Partial application](/static/content/slides/haskell-molecules/partial.png)
|
||||
![Partial application](partial.png)
|
||||
|
||||
```haskell ignore
|
||||
λ> :type makeOxygen
|
||||
|
@ -119,7 +119,7 @@ makeOxygen O O :: O₂
|
|||
|
||||
---
|
||||
|
||||
![Combustion](/static/content/slides/haskell-molecules/combustion.png)
|
||||
![Combustion](combustion.png)
|
||||
|
||||
```haskell ignore
|
||||
λ> :type burnOxygen
|
||||
|
@ -137,7 +137,7 @@ burnOxygen C O₂ :: CO₂
|
|||
---
|
||||
|
||||
|
||||
![Two functions](/static/content/slides/haskell-molecules/two-fs.png)
|
||||
![Two functions](two-fs.png)
|
||||
|
||||
```haskell
|
||||
f1 :: O -> O₂
|
||||
|
@ -149,7 +149,7 @@ f2 = burnOxygen C
|
|||
|
||||
---
|
||||
|
||||
![Composition](/static/content/slides/haskell-molecules/composition.png)
|
||||
![Composition](composition.png)
|
||||
|
||||
|
||||
```haskell
|
||||
|
@ -158,7 +158,7 @@ f3 = f2 . f1
|
|||
|
||||
---
|
||||
|
||||
![Composition2](/static/content/slides/haskell-molecules/composition2.png)
|
||||
![Composition2](composition2.png)
|
||||
|
||||
```haskell
|
||||
f3 :: O -> CO₂
|
||||
|
@ -194,7 +194,7 @@ Diagnostics:
|
|||
|
||||
---
|
||||
|
||||
![Composition3](/static/content/slides/haskell-molecules/composition3.png)
|
||||
![Composition3](composition3.png)
|
||||
|
||||
```haskell ignore
|
||||
f3' o = f2 . f1 o
|
||||
|
@ -202,7 +202,7 @@ f3' o = f2 . f1 o
|
|||
|
||||
---
|
||||
|
||||
![Funnel](/static/content/slides/haskell-molecules/funnel.png)
|
||||
![Funnel](funnel.png)
|
||||
|
||||
```haskell ignore
|
||||
f3' o = f2 . f1 $ o
|
||||
|
@ -228,11 +228,11 @@ f $ x = f x
|
|||
|
||||
## Isotopes
|
||||
|
||||
![Heavy water](/static/content/slides/haskell-molecules/heavy-water.gif)
|
||||
![Heavy water](heavy-water.gif)
|
||||
|
||||
---
|
||||
|
||||
![Isotopes](/static/content/slides/haskell-molecules/isotopes.png)
|
||||
![Isotopes](isotopes.png)
|
||||
|
||||
---
|
||||
|
||||
|
@ -531,7 +531,7 @@ data List a = () | (a) | (a, a) | (a, a, a) | ...
|
|||
|
||||
### Traits
|
||||
|
||||
![Neon colors](/static/content/slides/haskell-molecules/neon.png)
|
||||
![Neon colors](neon.png)
|
||||
|
||||
---
|
||||
|
||||
|
@ -663,11 +663,11 @@ mixNoble n1 n2 = toColor n1 <> "-" <> toColor n2
|
|||
|
||||
## Shrodinger's cat
|
||||
|
||||
![Cat in a box](/static/content/slides/haskell-molecules/cat.png)
|
||||
![Cat in a box](cat.png)
|
||||
|
||||
---
|
||||
|
||||
![Cat in a box](/static/content/slides/haskell-molecules/cat.png)
|
||||
![Cat in a box](cat.png)
|
||||
|
||||
```haskell
|
||||
data Box a
|
||||
|
@ -678,7 +678,7 @@ data Box a
|
|||
|
||||
---
|
||||
|
||||
![Map](/static/content/slides/haskell-molecules/map.png)
|
||||
![Map](map.png)
|
||||
|
||||
```haskell
|
||||
data Cat = Cat String deriving Show
|
||||
|
@ -687,7 +687,7 @@ data Dog = Dog String deriving Show
|
|||
|
||||
---
|
||||
|
||||
![Map](/static/content/slides/haskell-molecules/map.png)
|
||||
![Map](map.png)
|
||||
|
||||
```haskell
|
||||
class Mappable box where
|
||||
|
@ -720,11 +720,11 @@ instance Mappable [] where
|
|||
map' f xs = [f x | x <- xs]
|
||||
```
|
||||
|
||||
![Cat list](/static/content/slides/haskell-molecules/cat-list.png)
|
||||
![Cat list](cat-list.png)
|
||||
|
||||
---
|
||||
|
||||
![Cat list](/static/content/slides/haskell-molecules/cat-list.png)
|
||||
![Cat list](cat-list.png)
|
||||
|
||||
```haskell ignore
|
||||
λ> a = Has $ Cat "cat"
|
||||
|
@ -766,11 +766,11 @@ Has (Dog "a")
|
|||
|
||||
-----
|
||||
|
||||
![Cat merge](/static/content/slides/haskell-molecules/cat-merge.png)
|
||||
![Cat merge](cat-merge.png)
|
||||
|
||||
---
|
||||
|
||||
![Cat merge](/static/content/slides/haskell-molecules/cat-merge.png)
|
||||
![Cat merge](cat-merge.png)
|
||||
|
||||
```haskell
|
||||
merge :: Cat -> Cat -> Dog
|
||||
|
@ -789,7 +789,7 @@ catB = Cat "Nyaa"
|
|||
Dog "Meow & Nyaa"
|
||||
```
|
||||
|
||||
![Cat merge](/static/content/slides/haskell-molecules/merge.png)
|
||||
![Cat merge](merge.png)
|
||||
|
||||
---
|
||||
|
||||
|
@ -799,7 +799,7 @@ boxB = Has $ Cat "Nyaa" :: Box Cat
|
|||
empty = Empty :: Box Cat
|
||||
```
|
||||
|
||||
![Cat merge](/static/content/slides/haskell-molecules/merge-box.png)
|
||||
![Cat merge](merge-box.png)
|
||||
|
||||
---
|
||||
|
||||
|
@ -809,7 +809,7 @@ boxB = Has $ Cat "Nyaa" :: Box Cat
|
|||
empty = Empty :: Box Cat
|
||||
```
|
||||
|
||||
![Cat merge](/static/content/slides/haskell-molecules/merge-box.png)
|
||||
![Cat merge](merge-box.png)
|
||||
|
||||
```haskell ignore
|
||||
λ> merge boxA catB
|
||||
|
@ -829,7 +829,7 @@ boxB = Has $ Cat "Nyaa" :: Box Cat
|
|||
empty = Empty :: Box Cat
|
||||
```
|
||||
|
||||
![Cat merge](/static/content/slides/haskell-molecules/merge-prim.png)
|
||||
![Cat merge](merge-prim.png)
|
||||
|
||||
```haskell
|
||||
merge' :: Box Cat -> Box Cat -> Box Dog
|
||||
|
@ -847,7 +847,7 @@ merge' _ Empty = Empty
|
|||
merge' (Has c1) (Has c2) = merge c1 c2
|
||||
```
|
||||
|
||||
![Cat merge](/static/content/slides/haskell-molecules/merge-prim.png)
|
||||
![Cat merge](merge-prim.png)
|
||||
|
||||
```haskell ignore
|
||||
λ> merge' boxA boxB
|
||||
|
@ -866,7 +866,7 @@ class Appliable box where
|
|||
apply' :: box (a -> b) -> box a -> box b
|
||||
```
|
||||
|
||||
![Wrap apply](/static/content/slides/haskell-molecules/wrap-apply.png)
|
||||
![Wrap apply](wrap-apply.png)
|
||||
|
||||
---
|
||||
|
||||
|
@ -970,11 +970,11 @@ Has (Dog "Meow & Nyaa")
|
|||
Empty
|
||||
```
|
||||
|
||||
![Wrap apply](/static/content/slides/haskell-molecules/wrap-apply.png)
|
||||
![Wrap apply](wrap-apply.png)
|
||||
|
||||
---
|
||||
|
||||
![Wrap apply](/static/content/slides/haskell-molecules/wrap-apply.png)
|
||||
![Wrap apply](wrap-apply.png)
|
||||
|
||||
```haskell
|
||||
merge4 :: Cat -> Cat -> Cat -> Cat -> Dog
|
||||
|
@ -1008,11 +1008,11 @@ killOrSave cat@(Cat name) = case name of
|
|||
_ -> Has cat
|
||||
```
|
||||
|
||||
![Kill](/static/content/slides/haskell-molecules/kill.png)
|
||||
![Kill](kill.png)
|
||||
|
||||
---
|
||||
|
||||
![Kill](/static/content/slides/haskell-molecules/kill.png)
|
||||
![Kill](kill.png)
|
||||
|
||||
```haskell ignore
|
||||
λ> wrap killOrSave `apply'` Has (Cat "Meow")
|
||||
|
@ -1059,11 +1059,11 @@ instance Chainable [] where
|
|||
chain xs f = [x' | x <- xs, x' <- f x]
|
||||
```
|
||||
|
||||
![Save-kill](/static/content/slides/haskell-molecules/save-kill.png)
|
||||
![Save-kill](save-kill.png)
|
||||
|
||||
---
|
||||
|
||||
![Save-kill](/static/content/slides/haskell-molecules/save-kill.png)
|
||||
![Save-kill](save-kill.png)
|
||||
|
||||
```haskell
|
||||
kill :: Cat -> Box Cat
|
||||
|
@ -1075,7 +1075,7 @@ save = Has
|
|||
|
||||
---
|
||||
|
||||
![Save-kill](/static/content/slides/haskell-molecules/save-kill.png)
|
||||
![Save-kill](save-kill.png)
|
||||
|
||||
```haskell ignore
|
||||
λ> boxA `chain` save `chain` save `chain` save
|
||||
|
@ -1134,15 +1134,15 @@ clone (Cat c) = [Cat $ c <> "L" , Cat $ c <> "R"]
|
|||
[]
|
||||
```
|
||||
|
||||
![Chain list](/static/content/slides/haskell-molecules/chain-list.png)
|
||||
![Chain list](chain-list.png)
|
||||
|
||||
---
|
||||
|
||||
![Callbacks](/static/content/slides/haskell-molecules/callbacks.jpg)
|
||||
![Callbacks](callbacks.jpg)
|
||||
|
||||
-----
|
||||
|
||||
![This was all dream](/static/content/slides/haskell-molecules/cave.png)
|
||||
![This was all dream](cave.png)
|
||||
|
||||
---
|
||||
|
||||
|
@ -1152,7 +1152,7 @@ What's a `Mappable`?
|
|||
|
||||
It's a `Functor`
|
||||
|
||||
![It's a Functor](/static/content/slides/haskell-molecules/its-functor.png)
|
||||
![It's a Functor](its-functor.png)
|
||||
|
||||
---
|
||||
|
||||
|
@ -1197,13 +1197,13 @@ It's a `Functor`
|
|||
|
||||
It's a `Functor`
|
||||
|
||||
![Tardis](/static/content/slides/haskell-molecules/tardis.jpg)
|
||||
![Tardis](tardis.jpg)
|
||||
|
||||
---
|
||||
|
||||
It's a `Functor`
|
||||
|
||||
![Tardis inside](/static/content/slides/haskell-molecules/tardis-inside.jpg)
|
||||
![Tardis inside](tardis-inside.jpg)
|
||||
|
||||
---
|
||||
|
||||
|
@ -1212,7 +1212,7 @@ main :: IO ()
|
|||
main = print "Hello World!"
|
||||
```
|
||||
|
||||
![Tardis inside](/static/content/slides/haskell-molecules/io.png)
|
||||
![Tardis inside](io.png)
|
||||
|
||||
---
|
||||
|
||||
|
@ -1222,7 +1222,7 @@ What's a `Appliable`?
|
|||
|
||||
It's an Applicative
|
||||
|
||||
![It's an applicative](/static/content/slides/haskell-molecules/its-applicative.png)
|
||||
![It's an applicative](its-applicative.png)
|
||||
|
||||
---
|
||||
|
||||
|
@ -1296,7 +1296,7 @@ What's a `Chainable`?
|
|||
|
||||
It's a monad
|
||||
|
||||
![It's a monad](/static/content/slides/haskell-molecules/its-monad.png)
|
||||
![It's a monad](its-monad.png)
|
||||
|
||||
---
|
||||
|
||||
|
@ -1342,7 +1342,7 @@ It's a monad
|
|||
|
||||
---
|
||||
|
||||
![Just one more typeclass bro](/static/content/slides/haskell-molecules/pepe.png)
|
||||
![Just one more typeclass bro](pepe.png)
|
||||
|
||||
-----
|
||||
|
21
js/package.json
Normal file
21
js/package.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "js",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"leaflet": "^1.9.4",
|
||||
"leaflet.markercluster": "^1.5.3",
|
||||
"reveal.js": "^5.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/leaflet": "^1.9.8",
|
||||
"@types/leaflet.markercluster": "^1.5.4"
|
||||
}
|
||||
}
|
|
@ -2,6 +2,8 @@ import L from 'leaflet';
|
|||
import 'leaflet.markercluster'
|
||||
|
||||
|
||||
// PHOTOS
|
||||
|
||||
interface Photo extends L.LatLngLiteral {
|
||||
thumbnail: string;
|
||||
photoUrl: string;
|
||||
|
@ -90,5 +92,38 @@ if (L.MarkerClusterGroup) {
|
|||
L.photo.cluster = function (options: any) {
|
||||
return new L.Photo.Cluster!(options);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MAP
|
||||
|
||||
const template = `
|
||||
<div class="popup">
|
||||
<a href="{photo}">
|
||||
<img width="{width}" height="{height}" src="{photo}" alt="" />
|
||||
<div class="meta">
|
||||
<span class="date">{date}</span><span class="caption">{caption}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const map = L.map('map').setView([51.85, 16.57], 13);
|
||||
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
}).addTo(map);
|
||||
|
||||
const photoLayer = L.photo.cluster().on('click', function(evt) {
|
||||
evt.layer.bindPopup(L.Util.template(template, evt.layer.photo)).openPopup();
|
||||
});
|
||||
|
||||
async function loadData() {
|
||||
const data = await fetch('/static/map/data.json');
|
||||
if (!data.ok) return;
|
||||
|
||||
return await data.json()
|
||||
}
|
||||
|
||||
// Add photos to the map
|
||||
loadData().then(data => photoLayer.add(data).addTo(map));
|
59
js/pnpm-lock.yaml
Normal file
59
js/pnpm-lock.yaml
Normal file
|
@ -0,0 +1,59 @@
|
|||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
leaflet:
|
||||
specifier: ^1.9.4
|
||||
version: 1.9.4
|
||||
leaflet.markercluster:
|
||||
specifier: ^1.5.3
|
||||
version: 1.5.3(leaflet@1.9.4)
|
||||
reveal.js:
|
||||
specifier: ^5.0.5
|
||||
version: 5.0.5
|
||||
|
||||
devDependencies:
|
||||
'@types/leaflet':
|
||||
specifier: ^1.9.8
|
||||
version: 1.9.8
|
||||
'@types/leaflet.markercluster':
|
||||
specifier: ^1.5.4
|
||||
version: 1.5.4
|
||||
|
||||
packages:
|
||||
|
||||
/@types/geojson@7946.0.14:
|
||||
resolution: {integrity: sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==}
|
||||
dev: true
|
||||
|
||||
/@types/leaflet.markercluster@1.5.4:
|
||||
resolution: {integrity: sha512-tfMP8J62+wfsVLDLGh5Zh1JZxijCaBmVsMAX78MkLPwvPitmZZtSin5aWOVRhZrCS+pEOZwNzexbfWXlY+7yjg==}
|
||||
dependencies:
|
||||
'@types/leaflet': 1.9.8
|
||||
dev: true
|
||||
|
||||
/@types/leaflet@1.9.8:
|
||||
resolution: {integrity: sha512-EXdsL4EhoUtGm2GC2ZYtXn+Fzc6pluVgagvo2VC1RHWToLGlTRwVYoDpqS/7QXa01rmDyBjJk3Catpf60VMkwg==}
|
||||
dependencies:
|
||||
'@types/geojson': 7946.0.14
|
||||
dev: true
|
||||
|
||||
/leaflet.markercluster@1.5.3(leaflet@1.9.4):
|
||||
resolution: {integrity: sha512-vPTw/Bndq7eQHjLBVlWpnGeLa3t+3zGiuM7fJwCkiMFq+nmRuG3RI3f7f4N4TDX7T4NpbAXpR2+NTRSEGfCSeA==}
|
||||
peerDependencies:
|
||||
leaflet: ^1.3.1
|
||||
dependencies:
|
||||
leaflet: 1.9.4
|
||||
dev: false
|
||||
|
||||
/leaflet@1.9.4:
|
||||
resolution: {integrity: sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==}
|
||||
dev: false
|
||||
|
||||
/reveal.js@5.0.5:
|
||||
resolution: {integrity: sha512-MPWPV/cRlkZhh72dAGYv/bUCr9ulwM2/ucCqiL/KN4tvhb6VvN49iwOyWHE08wppj8lMQXi2xbS3kyKgfyTYqg==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
dev: false
|
7
js/reveal.js
Normal file
7
js/reveal.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import Reveal from 'reveal.js';
|
||||
|
||||
|
||||
Reveal.initialize({
|
||||
hash: true,
|
||||
slideNumber: true,
|
||||
});
|
47
package.json
47
package.json
|
@ -1,47 +0,0 @@
|
|||
{
|
||||
"name": "kamoshi.org",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/mdx": "^2.1.1",
|
||||
"@astrojs/sitemap": "^3.1.1",
|
||||
"@astrojs/svelte": "^5.2.0",
|
||||
"@citation-js/plugin-bibtex": "^0.7.9",
|
||||
"@js-temporal/polyfill": "^0.4.4",
|
||||
"astro": "^4.4.14",
|
||||
"astro-pagefind": "^1.4.0",
|
||||
"chart.js": "^4.4.2",
|
||||
"citation-js": "^0.7.9",
|
||||
"leaflet": "^1.9.4",
|
||||
"leaflet.markercluster": "^1.5.3",
|
||||
"mdast-util-to-string": "^4.0.0",
|
||||
"purify-ts": "^2.0.3",
|
||||
"rehype-katex": "^7.0.0",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"rehype-stringify": "^10.0.0",
|
||||
"remark-directive": "^3.0.0",
|
||||
"remark-emoji": "^4.0.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-math": "^6.0.0",
|
||||
"remark-parse": "^11.0.0",
|
||||
"remark-rehype": "^11.1.0",
|
||||
"reveal.js": "^5.0.5",
|
||||
"svelte": "5.0.0-next.72",
|
||||
"unified": "^11.0.4",
|
||||
"unist-util-visit": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/leaflet": "^1.9.8",
|
||||
"@types/leaflet.markercluster": "^1.5.4",
|
||||
"@types/reveal.js": "^5.0.1",
|
||||
"@types/unist": "^3.0.2",
|
||||
"pagefind": "^1.0.4",
|
||||
"prettier": "^3.2.5",
|
||||
"sass": "^1.71.1",
|
||||
"sharp": "^0.33.2"
|
||||
}
|
||||
}
|
4860
pnpm-lock.yaml
4860
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
60
public/static/css/MarkerCluster.Default.css
Normal file
60
public/static/css/MarkerCluster.Default.css
Normal file
|
@ -0,0 +1,60 @@
|
|||
.marker-cluster-small {
|
||||
background-color: rgba(181, 226, 140, 0.6);
|
||||
}
|
||||
.marker-cluster-small div {
|
||||
background-color: rgba(110, 204, 57, 0.6);
|
||||
}
|
||||
|
||||
.marker-cluster-medium {
|
||||
background-color: rgba(241, 211, 87, 0.6);
|
||||
}
|
||||
.marker-cluster-medium div {
|
||||
background-color: rgba(240, 194, 12, 0.6);
|
||||
}
|
||||
|
||||
.marker-cluster-large {
|
||||
background-color: rgba(253, 156, 115, 0.6);
|
||||
}
|
||||
.marker-cluster-large div {
|
||||
background-color: rgba(241, 128, 23, 0.6);
|
||||
}
|
||||
|
||||
/* IE 6-8 fallback colors */
|
||||
.leaflet-oldie .marker-cluster-small {
|
||||
background-color: rgb(181, 226, 140);
|
||||
}
|
||||
.leaflet-oldie .marker-cluster-small div {
|
||||
background-color: rgb(110, 204, 57);
|
||||
}
|
||||
|
||||
.leaflet-oldie .marker-cluster-medium {
|
||||
background-color: rgb(241, 211, 87);
|
||||
}
|
||||
.leaflet-oldie .marker-cluster-medium div {
|
||||
background-color: rgb(240, 194, 12);
|
||||
}
|
||||
|
||||
.leaflet-oldie .marker-cluster-large {
|
||||
background-color: rgb(253, 156, 115);
|
||||
}
|
||||
.leaflet-oldie .marker-cluster-large div {
|
||||
background-color: rgb(241, 128, 23);
|
||||
}
|
||||
|
||||
.marker-cluster {
|
||||
background-clip: padding-box;
|
||||
border-radius: 20px;
|
||||
}
|
||||
.marker-cluster div {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin-left: 5px;
|
||||
margin-top: 5px;
|
||||
|
||||
text-align: center;
|
||||
border-radius: 15px;
|
||||
font: 12px "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||
}
|
||||
.marker-cluster span {
|
||||
line-height: 30px;
|
||||
}
|
14
public/static/css/MarkerCluster.css
Normal file
14
public/static/css/MarkerCluster.css
Normal file
|
@ -0,0 +1,14 @@
|
|||
.leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker-shadow {
|
||||
-webkit-transition: -webkit-transform 0.3s ease-out, opacity 0.3s ease-in;
|
||||
-moz-transition: -moz-transform 0.3s ease-out, opacity 0.3s ease-in;
|
||||
-o-transition: -o-transform 0.3s ease-out, opacity 0.3s ease-in;
|
||||
transition: transform 0.3s ease-out, opacity 0.3s ease-in;
|
||||
}
|
||||
|
||||
.leaflet-cluster-spider-leg {
|
||||
/* stroke-dashoffset (duration and function) should match with leaflet-marker-icon transform in order to track it exactly */
|
||||
-webkit-transition: -webkit-stroke-dashoffset 0.3s ease-out, -webkit-stroke-opacity 0.3s ease-in;
|
||||
-moz-transition: -moz-stroke-dashoffset 0.3s ease-out, -moz-stroke-opacity 0.3s ease-in;
|
||||
-o-transition: -o-stroke-dashoffset 0.3s ease-out, -o-stroke-opacity 0.3s ease-in;
|
||||
transition: stroke-dashoffset 0.3s ease-out, stroke-opacity 0.3s ease-in;
|
||||
}
|
600
public/static/css/leaflet.css
Normal file
600
public/static/css/leaflet.css
Normal file
|
@ -0,0 +1,600 @@
|
|||
/* required styles */
|
||||
|
||||
.leaflet-pane,
|
||||
.leaflet-tile,
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow,
|
||||
.leaflet-tile-container,
|
||||
.leaflet-pane > svg,
|
||||
.leaflet-pane > canvas,
|
||||
.leaflet-zoom-box,
|
||||
.leaflet-image-layer,
|
||||
.leaflet-layer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
.leaflet-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
.leaflet-tile,
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow {
|
||||
user-select: none;
|
||||
-webkit-user-drag: none;
|
||||
}
|
||||
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
|
||||
.leaflet-safari .leaflet-tile {
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
}
|
||||
/* hack that prevents hw layers "stretching" when loading new tiles */
|
||||
.leaflet-safari .leaflet-tile-container {
|
||||
width: 1600px;
|
||||
height: 1600px;
|
||||
-webkit-transform-origin: 0 0;
|
||||
}
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow {
|
||||
display: block;
|
||||
}
|
||||
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
|
||||
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
|
||||
.leaflet-container .leaflet-overlay-pane svg {
|
||||
max-width: none !important;
|
||||
max-height: none !important;
|
||||
}
|
||||
.leaflet-container .leaflet-marker-pane img,
|
||||
.leaflet-container .leaflet-shadow-pane img,
|
||||
.leaflet-container .leaflet-tile-pane img,
|
||||
.leaflet-container img.leaflet-image-layer,
|
||||
.leaflet-container .leaflet-tile {
|
||||
max-width: none !important;
|
||||
max-height: none !important;
|
||||
width: auto;
|
||||
padding: 0;
|
||||
}
|
||||
.leaflet-container img.leaflet-tile {
|
||||
/* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */
|
||||
mix-blend-mode: plus-lighter;
|
||||
}
|
||||
|
||||
.leaflet-container.leaflet-touch-zoom {
|
||||
touch-action: pan-x pan-y;
|
||||
}
|
||||
.leaflet-container.leaflet-touch-drag {
|
||||
/* Fallback for FF which doesn't support pinch-zoom */
|
||||
touch-action: none;
|
||||
touch-action: pinch-zoom;
|
||||
}
|
||||
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
|
||||
touch-action: none;
|
||||
}
|
||||
.leaflet-container {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.leaflet-container a {
|
||||
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
|
||||
}
|
||||
.leaflet-tile {
|
||||
visibility: hidden;
|
||||
}
|
||||
.leaflet-tile-loaded {
|
||||
visibility: inherit;
|
||||
}
|
||||
.leaflet-zoom-box {
|
||||
width: 0;
|
||||
height: 0;
|
||||
box-sizing: border-box;
|
||||
z-index: 800;
|
||||
}
|
||||
|
||||
.leaflet-pane { z-index: 400; }
|
||||
|
||||
.leaflet-tile-pane { z-index: 200; }
|
||||
.leaflet-overlay-pane { z-index: 400; }
|
||||
.leaflet-shadow-pane { z-index: 500; }
|
||||
.leaflet-marker-pane { z-index: 600; }
|
||||
.leaflet-tooltip-pane { z-index: 650; }
|
||||
.leaflet-popup-pane { z-index: 700; }
|
||||
|
||||
.leaflet-map-pane canvas { z-index: 100; }
|
||||
.leaflet-map-pane svg { z-index: 200; }
|
||||
|
||||
|
||||
/* control positioning */
|
||||
|
||||
.leaflet-control {
|
||||
position: relative;
|
||||
z-index: 800;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.leaflet-top,
|
||||
.leaflet-bottom {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
}
|
||||
.leaflet-top {
|
||||
top: 0;
|
||||
}
|
||||
.leaflet-right {
|
||||
right: 0;
|
||||
}
|
||||
.leaflet-bottom {
|
||||
bottom: 0;
|
||||
}
|
||||
.leaflet-left {
|
||||
left: 0;
|
||||
}
|
||||
.leaflet-control {
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
.leaflet-right .leaflet-control {
|
||||
float: right;
|
||||
}
|
||||
.leaflet-top .leaflet-control {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.leaflet-bottom .leaflet-control {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.leaflet-left .leaflet-control {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.leaflet-right .leaflet-control {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
/* zoom and fade animations */
|
||||
|
||||
.leaflet-fade-anim .leaflet-popup {
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s linear;
|
||||
}
|
||||
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
|
||||
opacity: 1;
|
||||
}
|
||||
.leaflet-zoom-animated {
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
svg.leaflet-zoom-animated {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-zoom-animated {
|
||||
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
|
||||
}
|
||||
.leaflet-zoom-anim .leaflet-tile,
|
||||
.leaflet-pan-anim .leaflet-tile {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-zoom-hide {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
|
||||
/* cursors */
|
||||
|
||||
.leaflet-interactive {
|
||||
cursor: pointer;
|
||||
}
|
||||
.leaflet-grab {
|
||||
cursor: grab;
|
||||
}
|
||||
.leaflet-crosshair,
|
||||
.leaflet-crosshair .leaflet-interactive {
|
||||
cursor: crosshair;
|
||||
}
|
||||
.leaflet-popup-pane,
|
||||
.leaflet-control {
|
||||
cursor: auto;
|
||||
}
|
||||
.leaflet-dragging .leaflet-grab,
|
||||
.leaflet-dragging .leaflet-grab .leaflet-interactive,
|
||||
.leaflet-dragging .leaflet-marker-draggable {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/* marker & overlays interactivity */
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow,
|
||||
.leaflet-image-layer,
|
||||
.leaflet-pane > svg path,
|
||||
.leaflet-tile-container {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.leaflet-marker-icon.leaflet-interactive,
|
||||
.leaflet-image-layer.leaflet-interactive,
|
||||
.leaflet-pane > svg path.leaflet-interactive,
|
||||
svg.leaflet-image-layer.leaflet-interactive path {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* visual tweaks */
|
||||
|
||||
.leaflet-container {
|
||||
background: #ddd;
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.leaflet-container a {
|
||||
color: #0078A8;
|
||||
}
|
||||
.leaflet-zoom-box {
|
||||
border: 2px dotted #38f;
|
||||
background: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
|
||||
/* general typography */
|
||||
.leaflet-container {
|
||||
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
|
||||
/* general toolbar styles */
|
||||
|
||||
.leaflet-bar {
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.leaflet-bar a {
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #ccc;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
.leaflet-bar a,
|
||||
.leaflet-control-layers-toggle {
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
display: block;
|
||||
}
|
||||
.leaflet-bar a:hover,
|
||||
.leaflet-bar a:focus {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
.leaflet-bar a:first-child {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
.leaflet-bar a:last-child {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-bottom: none;
|
||||
}
|
||||
.leaflet-bar a.leaflet-disabled {
|
||||
cursor: default;
|
||||
background-color: #f4f4f4;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-bar a {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
.leaflet-touch .leaflet-bar a:first-child {
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 2px;
|
||||
}
|
||||
.leaflet-touch .leaflet-bar a:last-child {
|
||||
border-bottom-left-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
}
|
||||
|
||||
/* zoom control */
|
||||
|
||||
.leaflet-control-zoom-in,
|
||||
.leaflet-control-zoom-out {
|
||||
font: bold 18px 'Lucida Console', Monaco, monospace;
|
||||
text-indent: 1px;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
|
||||
/* layers control */
|
||||
|
||||
.leaflet-control-layers {
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.leaflet-control-layers-toggle {
|
||||
background-image: url(images/layers.png);
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
.leaflet-retina .leaflet-control-layers-toggle {
|
||||
background-image: url(images/layers-2x.png);
|
||||
background-size: 26px 26px;
|
||||
}
|
||||
.leaflet-touch .leaflet-control-layers-toggle {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
.leaflet-control-layers .leaflet-control-layers-list,
|
||||
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
|
||||
display: none;
|
||||
}
|
||||
.leaflet-control-layers-expanded .leaflet-control-layers-list {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
.leaflet-control-layers-list {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.leaflet-control-layers-expanded {
|
||||
padding: 6px 10px 6px 6px;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
}
|
||||
.leaflet-control-layers-scrollbar {
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.leaflet-control-layers-selector {
|
||||
margin-top: 2px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
.leaflet-control-layers label {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-size: 1.08333em;
|
||||
}
|
||||
.leaflet-control-layers-separator {
|
||||
height: 0;
|
||||
border-top: 1px solid #ddd;
|
||||
margin: 5px -10px 5px -6px;
|
||||
}
|
||||
|
||||
/* Default icon URLs */
|
||||
.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */
|
||||
background-image: url(images/marker-icon.png);
|
||||
}
|
||||
|
||||
|
||||
/* attribution and scale controls */
|
||||
|
||||
.leaflet-container .leaflet-control-attribution {
|
||||
background: #fff;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
margin: 0;
|
||||
}
|
||||
.leaflet-control-attribution,
|
||||
.leaflet-control-scale-line {
|
||||
padding: 0 5px;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.leaflet-control-attribution a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.leaflet-control-attribution a:hover,
|
||||
.leaflet-control-attribution a:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.leaflet-attribution-flag {
|
||||
display: inline !important;
|
||||
vertical-align: baseline !important;
|
||||
width: 1em;
|
||||
height: 0.6669em;
|
||||
margin-right: 0.277em;
|
||||
}
|
||||
.leaflet-left .leaflet-control-scale {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.leaflet-bottom .leaflet-control-scale {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.leaflet-control-scale-line {
|
||||
border: 2px solid #777;
|
||||
border-top: none;
|
||||
line-height: 1.1;
|
||||
padding: 2px 5px 1px;
|
||||
white-space: nowrap;
|
||||
box-sizing: border-box;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
text-shadow: 1px 1px #fff;
|
||||
}
|
||||
.leaflet-control-scale-line:not(:first-child) {
|
||||
border-top: 2px solid #777;
|
||||
border-bottom: none;
|
||||
margin-top: -2px;
|
||||
}
|
||||
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
|
||||
border-bottom: 2px solid #777;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-attribution,
|
||||
.leaflet-touch .leaflet-control-layers,
|
||||
.leaflet-touch .leaflet-bar {
|
||||
box-shadow: none;
|
||||
}
|
||||
.leaflet-touch .leaflet-control-layers,
|
||||
.leaflet-touch .leaflet-bar {
|
||||
border: 2px solid rgba(0,0,0,0.2);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
|
||||
/* popup */
|
||||
|
||||
.leaflet-popup {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.leaflet-popup-content-wrapper {
|
||||
padding: 1px;
|
||||
text-align: left;
|
||||
border-radius: 12px;
|
||||
}
|
||||
.leaflet-popup-content {
|
||||
margin: 13px 24px 13px 20px;
|
||||
line-height: 1.3;
|
||||
font-size: 13px;
|
||||
font-size: 1.08333em;
|
||||
min-height: 1px;
|
||||
}
|
||||
.leaflet-popup-content p {
|
||||
margin: 17px 0;
|
||||
margin: 1.3em 0;
|
||||
}
|
||||
.leaflet-popup-tip-container {
|
||||
width: 40px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
margin-top: -1px;
|
||||
margin-left: -20px;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
.leaflet-popup-tip {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
padding: 1px;
|
||||
|
||||
margin: -10px auto 0;
|
||||
pointer-events: auto;
|
||||
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.leaflet-popup-content-wrapper,
|
||||
.leaflet-popup-tip {
|
||||
background: white;
|
||||
color: #333;
|
||||
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
|
||||
}
|
||||
.leaflet-container a.leaflet-popup-close-button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
border: none;
|
||||
text-align: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font: 16px/24px Tahoma, Verdana, sans-serif;
|
||||
color: #757575;
|
||||
text-decoration: none;
|
||||
background: transparent;
|
||||
}
|
||||
.leaflet-container a.leaflet-popup-close-button:hover,
|
||||
.leaflet-container a.leaflet-popup-close-button:focus {
|
||||
color: #585858;
|
||||
}
|
||||
.leaflet-popup-scrolled {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* div icon */
|
||||
|
||||
.leaflet-div-icon {
|
||||
background: #fff;
|
||||
border: 1px solid #666;
|
||||
}
|
||||
|
||||
|
||||
/* Tooltip */
|
||||
/* Base styles for the element that has a tooltip */
|
||||
.leaflet-tooltip {
|
||||
position: absolute;
|
||||
padding: 6px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 3px;
|
||||
color: #222;
|
||||
white-space: nowrap;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
|
||||
}
|
||||
.leaflet-tooltip.leaflet-interactive {
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.leaflet-tooltip-top:before,
|
||||
.leaflet-tooltip-bottom:before,
|
||||
.leaflet-tooltip-left:before,
|
||||
.leaflet-tooltip-right:before {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
border: 6px solid transparent;
|
||||
background: transparent;
|
||||
content: "";
|
||||
}
|
||||
|
||||
/* Directions */
|
||||
|
||||
.leaflet-tooltip-bottom {
|
||||
margin-top: 6px;
|
||||
}
|
||||
.leaflet-tooltip-top {
|
||||
margin-top: -6px;
|
||||
}
|
||||
.leaflet-tooltip-bottom:before,
|
||||
.leaflet-tooltip-top:before {
|
||||
left: 50%;
|
||||
margin-left: -6px;
|
||||
}
|
||||
.leaflet-tooltip-top:before {
|
||||
bottom: 0;
|
||||
margin-bottom: -12px;
|
||||
border-top-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-bottom:before {
|
||||
top: 0;
|
||||
margin-top: -12px;
|
||||
margin-left: -6px;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-left {
|
||||
margin-left: -6px;
|
||||
}
|
||||
.leaflet-tooltip-right {
|
||||
margin-left: 6px;
|
||||
}
|
||||
.leaflet-tooltip-left:before,
|
||||
.leaflet-tooltip-right:before {
|
||||
top: 50%;
|
||||
margin-top: -6px;
|
||||
}
|
||||
.leaflet-tooltip-left:before {
|
||||
right: 0;
|
||||
margin-right: -12px;
|
||||
border-left-color: #fff;
|
||||
}
|
||||
.leaflet-tooltip-right:before {
|
||||
left: 0;
|
||||
margin-left: -12px;
|
||||
border-right-color: #fff;
|
||||
}
|
||||
|
||||
/* Printing */
|
||||
|
||||
@media print {
|
||||
/* Prevent printers from removing background-images of controls. */
|
||||
.leaflet-control {
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
}
|
366
public/static/css/reveal.css
Normal file
366
public/static/css/reveal.css
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,27 +0,0 @@
|
|||
.leaflet-marker-photo {
|
||||
border: 2px solid #fff;
|
||||
box-shadow: 3px 3px 10px #888;
|
||||
|
||||
div {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
b {
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
right: -11px;
|
||||
color: #555;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
height: 12px;
|
||||
min-width: 12px;
|
||||
line-height: 12px;
|
||||
text-align: center;
|
||||
padding: 3px;
|
||||
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
|
||||
}
|
||||
}
|
12
src/assets/leaflet-photo/types.d.ts
vendored
12
src/assets/leaflet-photo/types.d.ts
vendored
|
@ -1,12 +0,0 @@
|
|||
import 'leaflet';
|
||||
|
||||
declare module 'leaflet' {
|
||||
class Photo extends L.FeatureGroup {
|
||||
static Cluster?: { new(...args: any[]): any } & L.Class;
|
||||
}
|
||||
|
||||
let photo: {
|
||||
(photos: Photo[], options: any): Photo;
|
||||
cluster?: (options?: any) => any;
|
||||
};
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
## かもし
|
||||
|
||||
初めましての方は初めまして、ポーランド出身で日本語を勉強している人です。
|
||||
間違いがあったらすみません。
|
||||
|
||||
こちらは個人的なウェブサイトで、「かもし」というのは個人サークル名といってもいいです。
|
||||
日本語を練習するため日本語を使って色々なことを書きます。
|
||||
英語も使います。趣味はプログラミングや日本語や日本の歌や同人など色々なことです。
|
||||
質問があったらメールを送信してくれてください。
|
|
@ -1,75 +0,0 @@
|
|||
import type { Node, Parent } from 'unist';
|
||||
import { unified } from "unified";
|
||||
import remarkParse from "remark-parse";
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkRehype from "remark-rehype";
|
||||
import rehypeRaw from "rehype-raw";
|
||||
import rehypeStringify from "rehype-stringify";
|
||||
import { visit } from "unist-util-visit";
|
||||
|
||||
|
||||
interface CodeNode extends Node {
|
||||
type: 'code';
|
||||
lang?: string;
|
||||
meta?: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
const ESCAPED_CHARS: {[key: string]: string} = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": '''
|
||||
};
|
||||
|
||||
const REGEX_HL_LINES = /\[([\s\d,|-]*)\]/;
|
||||
function transformCode(node: CodeNode, index: number, parent: Parent) {
|
||||
if (!node.meta || !REGEX_HL_LINES.test(node.meta)) return;
|
||||
|
||||
const langtag = node.lang ? ` class="${node.lang}" ` : ''
|
||||
const numbers = node.meta.match(REGEX_HL_LINES)![1];
|
||||
const escaped = node.value.replace(/[&<>"']/g, match => ESCAPED_CHARS[match] || '');
|
||||
parent.children[index] = {
|
||||
type: 'html',
|
||||
value: `<pre><code data-line-numbers="${numbers}"${langtag}>${escaped}</code></pre>`,
|
||||
} as any;
|
||||
}
|
||||
|
||||
function codePassthrough() {
|
||||
return (tree: Node, _: any) => {
|
||||
visit(tree, 'code', transformCode);
|
||||
}
|
||||
}
|
||||
|
||||
const renderer = unified()
|
||||
.use(remarkParse)
|
||||
.use(remarkGfm)
|
||||
.use(codePassthrough)
|
||||
.use(remarkRehype, {allowDangerousHtml: true})
|
||||
.use(rehypeRaw)
|
||||
.use(rehypeStringify);
|
||||
|
||||
|
||||
const SPLIT_H = /\n-----\n/;
|
||||
const SPLIT_V = /\n---\n/;
|
||||
|
||||
|
||||
function wrapSection(animate: boolean, id: number) {
|
||||
return (content: string): string => {
|
||||
return animate
|
||||
? `<section data-auto-animate data-auto-animate-id="${id}">${content}</section>`
|
||||
: `<section>${content}</section>`;
|
||||
}
|
||||
}
|
||||
|
||||
export function render(text: string, animate = false): string {
|
||||
const wrapOuter = wrapSection(false, 0);
|
||||
return text
|
||||
.split(SPLIT_H)
|
||||
.map(stacks => stacks.split(SPLIT_V).map(slide => String(renderer.processSync(slide))))
|
||||
.map((stack, i) => (stack.length > 1)
|
||||
? wrapOuter(stack.map(wrapSection(animate, i)).join(''))
|
||||
: wrapOuter(stack[0]))
|
||||
.join('');
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
[
|
||||
{
|
||||
"year": 2016,
|
||||
"months": [
|
||||
{
|
||||
"month": "May",
|
||||
"events": [
|
||||
"Started learning Japanese language"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"year": 2018,
|
||||
"months": [
|
||||
{
|
||||
"month": "September",
|
||||
"events": [
|
||||
"Started studying Computer Science"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"year": 2022,
|
||||
"months": [
|
||||
{
|
||||
"month": "February",
|
||||
"events": [
|
||||
"Completed bachelor's degree in Computer Science at Wrocław University of Science and Technology"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"year": 2023,
|
||||
"months": [
|
||||
{
|
||||
"month": "July",
|
||||
"events": [
|
||||
"Completed master's degree in Computer Science at Wrocław University of Science and Technology"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -1,10 +0,0 @@
|
|||
---
|
||||
interface Props {
|
||||
src: string;
|
||||
size?: 'normal' | 'big';
|
||||
}
|
||||
|
||||
const { src, size } = Astro.props;
|
||||
|
||||
---
|
||||
<img class="md-icon" class:list={[size]} src={src} alt=""/>
|
|
@ -1,3 +0,0 @@
|
|||
<small class="marginnote">
|
||||
<slot />
|
||||
</small>
|
|
@ -1,33 +0,0 @@
|
|||
---
|
||||
import timeline from '../assets/timeline.json';
|
||||
---
|
||||
<div class="sc-timeline">
|
||||
{timeline.map((year: any) => (
|
||||
<section class="sc-timeline__year">
|
||||
<h3 class="sc-timeline__year-header sticky font-medium">{year.year}</h3>
|
||||
|
||||
<div class="sc-timeline__year-container">
|
||||
{year.events && (
|
||||
<section class="sc-timeline__year-events">
|
||||
<ul class="sc-timeline__list">
|
||||
{year.events.map((event: any) => (
|
||||
<li class="sc-timeline__list-item">{event}</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{year.months?.map((month: any) => (
|
||||
<section class="sc-timeline__month">
|
||||
<h4 class="sc-timeline__month-header font-medium">{month.month}</h4>
|
||||
<ul class="sc-timeline__list">
|
||||
{month.events.map((event: any) => (
|
||||
<li class="sc-timeline__list-item">{event}</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
</div>
|
|
@ -1,54 +0,0 @@
|
|||
---
|
||||
interface Props {
|
||||
headings: Array<{
|
||||
depth: number;
|
||||
slug: string;
|
||||
text: string;
|
||||
}>
|
||||
}
|
||||
|
||||
type Heading = Props['headings'][number];
|
||||
type Nested = Heading & { children?: Heading[] };
|
||||
|
||||
const { headings } = Astro.props;
|
||||
|
||||
function fold(headings: Heading[]) {
|
||||
const toc = [] as Nested[];
|
||||
const map = new Map<number, Nested>();
|
||||
for (const h of headings) {
|
||||
const heading = { ...h };
|
||||
map.set(heading.depth, heading);
|
||||
if (heading.depth === 2)
|
||||
toc.push(heading)
|
||||
else {
|
||||
const backref = map.get(heading.depth - 1)!;
|
||||
backref.children
|
||||
? backref.children.push(heading)
|
||||
: backref.children = [heading];
|
||||
}
|
||||
}
|
||||
return toc;
|
||||
}
|
||||
---
|
||||
|
||||
<section class="p-toc">
|
||||
<h2>Content</h2>
|
||||
<nav>
|
||||
<ul class="p-toc__primary">
|
||||
{fold(headings).map(heading => (
|
||||
<li class="p-toc__primary-item">
|
||||
<a href={`#${heading.slug}`}>{heading.text}</a>
|
||||
{heading.children && (
|
||||
<ul class="p-toc__nested">
|
||||
{heading.children?.map(child => (
|
||||
<li class="p-toc__nested-item">
|
||||
<a href={`#${child.slug}`}>{child.text}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
</section>
|
|
@ -1,15 +0,0 @@
|
|||
---
|
||||
const year = new Date().getFullYear();
|
||||
const copy = `Copyright © ${year} Maciej Jur`;
|
||||
const mail = "maciej@kamoshi.org";
|
||||
const href = `mailto:${mail}`;
|
||||
---
|
||||
<footer class="footer">
|
||||
<div>
|
||||
<div set:html={copy}></div>
|
||||
<a href={href}>{mail}</a>
|
||||
</div>
|
||||
<a class="footer__cc-wrap" rel="license" href="http://creativecommons.org/licenses/by/4.0/">
|
||||
<img class="footer__cc-stamp" alt="Creative Commons License" width="88" height="31" src="/static/svg/by.svg"/>
|
||||
</a>
|
||||
</footer>
|
|
@ -1,19 +0,0 @@
|
|||
---
|
||||
import type { Maybe } from 'purify-ts';
|
||||
|
||||
interface Props {
|
||||
title: Maybe<string>;
|
||||
}
|
||||
|
||||
const { title } = Astro.props;
|
||||
---
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>{title.mapOrDefault(title => `${title} | kamoshi.org`, "kamoshi.org")}</title>
|
||||
|
||||
<link rel="sitemap" href="/sitemap-index.xml"/>
|
||||
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
||||
<link rel="icon" href="/favicon.ico" sizes="any">
|
|
@ -1,73 +0,0 @@
|
|||
---
|
||||
import Logotype from "./Logotype.astro";
|
||||
|
||||
|
||||
interface MenuItem {
|
||||
name: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
/** Config for the menu displayed in site navbar. */
|
||||
const menu: MenuItem[] = [
|
||||
{
|
||||
name: 'Posts',
|
||||
url: '/posts/',
|
||||
},
|
||||
{
|
||||
name: 'Slides',
|
||||
url: '/slides/',
|
||||
},
|
||||
{
|
||||
name: 'Wiki',
|
||||
url: '/wiki/',
|
||||
},
|
||||
{
|
||||
name: 'Map',
|
||||
url: '/map/',
|
||||
},
|
||||
{
|
||||
name: 'About',
|
||||
url: '/about/',
|
||||
},
|
||||
{
|
||||
name: 'Search',
|
||||
url: '/search/'
|
||||
}
|
||||
];
|
||||
---
|
||||
<nav class="p-nav">
|
||||
<input id="p-nav-toggle" type="checkbox" hidden>
|
||||
|
||||
<div class="p-nav__bar">
|
||||
<a href="/" class="p-nav__logo">
|
||||
<img class="p-nav__logo-icon" height="48px" width="51px" src="/static/svg/aya.svg" alt="">
|
||||
<div class="p-nav__logo-text">
|
||||
<div class="p-nav__logo-main">
|
||||
<Logotype />
|
||||
</div>
|
||||
<div class="p-nav__logo-sub" id="p-nav-splash">
|
||||
Doesn't require JavaScript!
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<label class="p-nav__burger" for="p-nav-toggle" tabindex="0">
|
||||
<span class="p-nav__burger-icon"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<menu class="p-nav__menu">
|
||||
{menu.map(item => (
|
||||
<li class="p-nav__menu-item">
|
||||
<a class="p-nav__menu-link" href={item.url}>
|
||||
{item.name}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</menu>
|
||||
|
||||
<script>
|
||||
import { bindSubtitle } from "../../utils/splash";
|
||||
bindSubtitle();
|
||||
</script>
|
||||
</nav>
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
import Text from '@assets/markdown/intro.md';
|
||||
---
|
||||
<section class="p-card intro-jp" lang="ja-JP">
|
||||
<Text/>
|
||||
</section>
|
|
@ -1,13 +0,0 @@
|
|||
---
|
||||
import image from "@assets/home/IMG_20231029_111650.jpg";
|
||||
import { Image } from "astro:assets";
|
||||
|
||||
|
||||
const alt = "Autumn park with colorful trees and fallen leaves";
|
||||
---
|
||||
<section class="p-card home-card-image">
|
||||
<h2 class="p-card__heading">Image of the Month</h2>
|
||||
<a href={image.src} class="home-card-image__link">
|
||||
<Image src={image} alt={alt} class="home-card-image__image" />
|
||||
</a>
|
||||
</section>
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
import Widget from './kanji.svelte';
|
||||
---
|
||||
<section class="p-card">
|
||||
<h2 class="p-card__heading">Kanji of the Day</h2>
|
||||
<div>
|
||||
<Widget client:load/>
|
||||
</div>
|
||||
</section>
|
|
@ -1,51 +0,0 @@
|
|||
type Ruby = Array<[string, string]>;
|
||||
|
||||
export interface KKLCEntry {
|
||||
id: number;
|
||||
char: string;
|
||||
keys: string[];
|
||||
senses: string[];
|
||||
onyomi: string[];
|
||||
kunyomi: string[];
|
||||
examples: Array<[string, Ruby]>;
|
||||
}
|
||||
|
||||
|
||||
async function chooseId(): Promise<number> {
|
||||
const date = new Date().toLocaleDateString('en');
|
||||
const data = new TextEncoder().encode(date);
|
||||
|
||||
const hash = await crypto.subtle.digest('SHA-256', data);
|
||||
|
||||
const hashArray = Array.from(new Uint8Array(hash));
|
||||
const hashValue = hashArray.reduce((acc, byte) => acc + byte, 0);
|
||||
|
||||
const min = 1;
|
||||
const max = 2300;
|
||||
return min + (hashValue % (max - min + 1));
|
||||
}
|
||||
|
||||
function tryGetCache(id: number) {
|
||||
const item = localStorage.getItem('kanji');
|
||||
if (!item) return;
|
||||
|
||||
const cache = JSON.parse(item);
|
||||
if (cache.id === id) {
|
||||
return cache.data;
|
||||
}
|
||||
}
|
||||
|
||||
function insertCache(id: number, data: KKLCEntry) {
|
||||
localStorage.setItem('kanji', JSON.stringify({ id, data }));
|
||||
}
|
||||
|
||||
export async function getKanji(): Promise<KKLCEntry> {
|
||||
const id = await chooseId();
|
||||
|
||||
const cache = tryGetCache(id);
|
||||
if (cache) return cache;
|
||||
|
||||
const data = await fetch(`/static/kanji/${id}.json`).then(res => res.json());
|
||||
insertCache(id, data);
|
||||
return data;
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { getKanji, type KKLCEntry } from './data.svelte.ts';
|
||||
|
||||
let state = $state<Promise<KKLCEntry>>(new Promise(() => {}));
|
||||
|
||||
$effect(() => void (state = getKanji()));
|
||||
</script>
|
||||
|
||||
|
||||
<div class="daily-kanji">
|
||||
{#await state}
|
||||
<div class="spinner-wrap">
|
||||
<div class="spinner"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div>
|
||||
</div>
|
||||
{:then state}
|
||||
<div class="info">
|
||||
<div class="info-char">
|
||||
{state.char}
|
||||
</div>
|
||||
<div class="info-meta">
|
||||
<div class="info-key">
|
||||
{state.keys.join(', ')}
|
||||
</div>
|
||||
<div class="info-on">
|
||||
{state.onyomi.join(', ')}
|
||||
</div>
|
||||
<div class="info-kun">
|
||||
{state.kunyomi.join(', ')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<table class="examples">
|
||||
<tbody>
|
||||
{#each state.examples as [meaning, example]}
|
||||
<tr>
|
||||
<td class="examples-ja">
|
||||
<ruby>
|
||||
{#each example as [expr, ruby]}{expr}<rt>{ruby||''}</rt>{/each}
|
||||
</ruby>
|
||||
</td>
|
||||
<td class="examples-en">
|
||||
{meaning}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
{/await}
|
||||
</div>
|
|
@ -1,29 +0,0 @@
|
|||
---
|
||||
import { Temporal } from "@js-temporal/polyfill";
|
||||
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
date: Temporal.ZonedDateTime;
|
||||
desc: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
const { title, date, desc, path } = Astro.props;
|
||||
|
||||
const short = date.toPlainDate().toString();
|
||||
const human = date.toLocaleString("en", { month: '2-digit', day: "2-digit" });
|
||||
---
|
||||
<a class="page-item" href={path}>
|
||||
<div class="page-item__header">
|
||||
<h3>{title}</h3>
|
||||
<time datetime={short}>
|
||||
{human}
|
||||
</time>
|
||||
</div>
|
||||
{desc && (
|
||||
<div class="page-item__desc">
|
||||
{desc}
|
||||
</div>
|
||||
)}
|
||||
</a>
|
26
src/components/search/pagefind.d.ts
vendored
26
src/components/search/pagefind.d.ts
vendored
|
@ -1,26 +0,0 @@
|
|||
interface Pagefind {
|
||||
search: (query: string) => Promise<PagefindResponse>;
|
||||
}
|
||||
|
||||
interface PagefindResult {
|
||||
id: string;
|
||||
data: () => Promise<PagefindDocument>;
|
||||
}
|
||||
|
||||
interface PagefindResponse {
|
||||
results: PagefindResult[];
|
||||
}
|
||||
|
||||
interface PagefindDocument {
|
||||
url: string;
|
||||
excerpt: string;
|
||||
filters: {
|
||||
author: string;
|
||||
};
|
||||
meta: {
|
||||
title: string;
|
||||
image: string;
|
||||
};
|
||||
content: string;
|
||||
word_count: number;
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
<script lang="ts">
|
||||
import type { ChangeEventHandler, UIEventHandler } from 'svelte/elements';
|
||||
|
||||
let client = $state<Pagefind>();
|
||||
let query = $state<string>('');
|
||||
let limit = $state<number>(10);
|
||||
let result = $derived(client?.search(query));
|
||||
|
||||
const require = (path: string) => import(/* @vite-ignore */path);
|
||||
|
||||
|
||||
function sync(): void {
|
||||
query = new URLSearchParams(window.location.search).get('q') || '';
|
||||
}
|
||||
|
||||
function onInput(): ChangeEventHandler<HTMLInputElement> {
|
||||
let debounce: number | undefined;
|
||||
|
||||
return event => {
|
||||
clearTimeout(debounce);
|
||||
const value = event.currentTarget.value;
|
||||
const url = new URL(window.location.href);
|
||||
(value)
|
||||
? url.searchParams.set('q', value)
|
||||
: url.searchParams.delete('q');
|
||||
debounce = setTimeout(() => window.history.pushState({}, '', url), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
function onScroll(): UIEventHandler<Window> {
|
||||
let throttle = Date.now();
|
||||
|
||||
return event => {
|
||||
const now = Date.now();
|
||||
if (throttle + 200 > now) return;
|
||||
|
||||
const { scrollHeight } = document.documentElement;
|
||||
const { innerHeight, scrollY } = event.currentTarget;
|
||||
|
||||
const distance = scrollHeight - (innerHeight + scrollY);
|
||||
if (distance < 100) {
|
||||
limit += 5;
|
||||
throttle = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
sync();
|
||||
require('/pagefind/pagefind.js').then(pf => client = pf);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<svelte:window
|
||||
on:popstate={sync}
|
||||
on:scroll={onScroll()}/>
|
||||
|
||||
{#snippet tile(data: PagefindDocument)}
|
||||
<a class="c-search__result" href={data.url}>
|
||||
<header class="c-search__header">
|
||||
<h2 class="c-search__title">
|
||||
{data.meta.title}
|
||||
</h2>
|
||||
</header>
|
||||
<div class="c-search__excerpt">
|
||||
{@html data.excerpt}
|
||||
</div>
|
||||
</a>
|
||||
{/snippet}
|
||||
|
||||
<article class="c-search">
|
||||
<h1>Search</h1>
|
||||
<input class="c-search__input" placeholder="Start typing here!"
|
||||
bind:value={query}
|
||||
on:input={onInput()}
|
||||
on:input={() => limit = 10}/>
|
||||
|
||||
{#if query && result}
|
||||
{#await result}
|
||||
Loading...
|
||||
{:then {results}}
|
||||
<section class="c-search__results">
|
||||
<div>Showing results for "{query}" ({results.length})</div>
|
||||
{#each results.slice(0, limit) as page (page.id)}
|
||||
{#await page.data()}
|
||||
Loading...
|
||||
{:then page}
|
||||
{@render tile(page)}
|
||||
{/await}
|
||||
{/each}
|
||||
</section>
|
||||
{/await}
|
||||
{:else}
|
||||
<div>No results to show yet...</div>
|
||||
{/if}
|
||||
</article>
|
|
@ -1,43 +0,0 @@
|
|||
---
|
||||
import { Maybe } from 'purify-ts/Maybe';
|
||||
import HeadingsList from './HeadingsList.astro';
|
||||
import type { MarkdownHeading } from 'astro';
|
||||
|
||||
|
||||
interface Props {
|
||||
headings: MarkdownHeading[];
|
||||
}
|
||||
|
||||
export type Nested = MarkdownHeading & { children?: MarkdownHeading[] };
|
||||
|
||||
|
||||
function fold(headings: MarkdownHeading[]): Nested[] {
|
||||
const toc = [] as Nested[];
|
||||
const map = new Map<number, Nested>();
|
||||
for (const h of headings) {
|
||||
const heading = { ...h };
|
||||
map.set(heading.depth, heading);
|
||||
if (heading.depth === 2)
|
||||
toc.push(heading)
|
||||
else {
|
||||
const backref = map.get(heading.depth - 1)!;
|
||||
backref.children
|
||||
? backref.children.push(heading)
|
||||
: backref.children = [heading];
|
||||
}
|
||||
}
|
||||
return toc;
|
||||
}
|
||||
|
||||
|
||||
const { headings } = Astro.props;
|
||||
---
|
||||
|
||||
<h2 class="link-tree__heading">
|
||||
<a class="link-tree__heading-text" href="#top">Content</a>
|
||||
</h2>
|
||||
<nav id="table-of-contents" class="link-tree__nav">
|
||||
<HeadingsList
|
||||
headings={Maybe.of(headings).map(fold)}
|
||||
/>
|
||||
</nav>
|
|
@ -1,28 +0,0 @@
|
|||
---
|
||||
import { Maybe } from 'purify-ts';
|
||||
import type { Nested } from './Headings.astro';
|
||||
|
||||
|
||||
interface Props {
|
||||
headings: Maybe<Nested[]>;
|
||||
}
|
||||
|
||||
|
||||
const { headings } = Astro.props;
|
||||
---
|
||||
|
||||
{headings
|
||||
.map(headings =>
|
||||
<ul class="link-tree__nav-list">
|
||||
{headings.map(heading =>
|
||||
<li class="link-tree__nav-list-item">
|
||||
<a class="link-tree__nav-list-text link" href={`#${heading.slug}`}>
|
||||
{heading.text}
|
||||
</a>
|
||||
<Astro.self headings={Maybe.fromNullable(heading.children)}/>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
)
|
||||
.extract()
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
---
|
||||
import PagesList from "./PagesList.astro";
|
||||
import type { Maybe } from "purify-ts/Maybe";
|
||||
import type { PagesProps } from "./PagesList.astro";
|
||||
import { pathify } from "@utils/tree";
|
||||
|
||||
|
||||
interface Props {
|
||||
heading: string;
|
||||
pages: Maybe<PagesProps>;
|
||||
}
|
||||
|
||||
const { heading, pages } = Astro.props;
|
||||
---
|
||||
|
||||
<h2 class="link-tree__heading">
|
||||
{pages.chain(x => x.prefix)
|
||||
.map(pathify)
|
||||
.mapOrDefault(href =>
|
||||
<a class="link-tree__heading-text" href={href}>{heading}</a>,
|
||||
<span class="link-tree__heading-text">{heading}</span>
|
||||
)}
|
||||
</h2>
|
||||
<nav class="link-tree__nav">
|
||||
{pages.map(pages => <PagesList {...pages} />).extract()}
|
||||
</nav>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue