feat: kanji of the day widget

This commit is contained in:
Maciej Jur 2024-02-02 23:37:49 +01:00
parent 68ff006eff
commit 5d71a7924c
Signed by: kamov
GPG key ID: 191CBFF5F72ECAFD
9 changed files with 238 additions and 43 deletions

View file

@ -15,7 +15,7 @@
"@astrojs/svelte": "^5.0.3",
"@citation-js/plugin-bibtex": "^0.7.8",
"@js-temporal/polyfill": "^0.4.4",
"astro": "^4.3.0",
"astro": "^4.3.2",
"astro-pagefind": "^1.4.0",
"chart.js": "^4.4.1",
"citation-js": "^0.7.8",
@ -33,7 +33,7 @@
"remark-parse": "^11.0.0",
"remark-rehype": "^11.1.0",
"reveal.js": "^5.0.4",
"svelte": "5.0.0-next.44",
"svelte": "5.0.0-next.45",
"unified": "^11.0.4",
"unist-util-visit": "^5.0.0"
},

View file

@ -7,10 +7,10 @@ settings:
dependencies:
'@astrojs/mdx':
specifier: ^2.1.1
version: 2.1.1(astro@4.3.0)
version: 2.1.1(astro@4.3.2)
'@astrojs/svelte':
specifier: ^5.0.3
version: 5.0.3(astro@4.3.0)(svelte@5.0.0-next.44)(typescript@5.3.3)(vite@5.0.12)
version: 5.0.3(astro@4.3.2)(svelte@5.0.0-next.45)(typescript@5.3.3)(vite@5.0.12)
'@citation-js/plugin-bibtex':
specifier: ^0.7.8
version: 0.7.8(@citation-js/core@0.7.6)
@ -18,11 +18,11 @@ dependencies:
specifier: ^0.4.4
version: 0.4.4
astro:
specifier: ^4.3.0
version: 4.3.0(sass@1.70.0)(typescript@5.3.3)
specifier: ^4.3.2
version: 4.3.2(sass@1.70.0)(typescript@5.3.3)
astro-pagefind:
specifier: ^1.4.0
version: 1.4.0(astro@4.3.0)
version: 1.4.0(astro@4.3.2)
chart.js:
specifier: ^4.4.1
version: 4.4.1
@ -72,8 +72,8 @@ dependencies:
specifier: ^5.0.4
version: 5.0.4
svelte:
specifier: 5.0.0-next.44
version: 5.0.0-next.44
specifier: 5.0.0-next.45
version: 5.0.0-next.45
unified:
specifier: ^11.0.4
version: 11.0.4
@ -143,7 +143,7 @@ packages:
- supports-color
dev: false
/@astrojs/mdx@2.1.1(astro@4.3.0):
/@astrojs/mdx@2.1.1(astro@4.3.2):
resolution: {integrity: sha512-AgGFdE7HOGmoFooGvMSatkA9FiSKwyVW7ImHot/bXJ6uAbFfu6iG2ht18Cf1pT22Hda/6iSCGWusFvBv0/EnKQ==}
engines: {node: '>=18.14.1'}
peerDependencies:
@ -152,7 +152,7 @@ packages:
'@astrojs/markdown-remark': 4.2.1
'@mdx-js/mdx': 3.0.0
acorn: 8.11.3
astro: 4.3.0(sass@1.70.0)(typescript@5.3.3)
astro: 4.3.2(sass@1.70.0)(typescript@5.3.3)
es-module-lexer: 1.4.1
estree-util-visit: 2.0.0
github-slugger: 2.0.0
@ -176,17 +176,17 @@ packages:
prismjs: 1.29.0
dev: false
/@astrojs/svelte@5.0.3(astro@4.3.0)(svelte@5.0.0-next.44)(typescript@5.3.3)(vite@5.0.12):
/@astrojs/svelte@5.0.3(astro@4.3.2)(svelte@5.0.0-next.45)(typescript@5.3.3)(vite@5.0.12):
resolution: {integrity: sha512-6TUBRUxmsEczKPBT6oDUAfvzuFCmITuhZfKPT5ZtOOyj9XOVnEnj/Iobd3ajKUbpWNYX7qZVAd1KMkmJc1Nhsg==}
engines: {node: '>=18.14.1'}
peerDependencies:
astro: ^4.0.0
svelte: ^4.0.0 || ^5.0.0-next.1
dependencies:
'@sveltejs/vite-plugin-svelte': 3.0.2(svelte@5.0.0-next.44)(vite@5.0.12)
astro: 4.3.0(sass@1.70.0)(typescript@5.3.3)
svelte: 5.0.0-next.44
svelte2tsx: 0.6.27(svelte@5.0.0-next.44)(typescript@5.3.3)
'@sveltejs/vite-plugin-svelte': 3.0.2(svelte@5.0.0-next.45)(vite@5.0.12)
astro: 4.3.2(sass@1.70.0)(typescript@5.3.3)
svelte: 5.0.0-next.45
svelte2tsx: 0.6.27(svelte@5.0.0-next.45)(typescript@5.3.3)
transitivePeerDependencies:
- supports-color
- typescript
@ -1189,7 +1189,7 @@ packages:
engines: {node: '>=10'}
dev: false
/@sveltejs/vite-plugin-svelte-inspector@2.0.0(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@5.0.0-next.44)(vite@5.0.12):
/@sveltejs/vite-plugin-svelte-inspector@2.0.0(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@5.0.0-next.45)(vite@5.0.12):
resolution: {integrity: sha512-gjr9ZFg1BSlIpfZ4PRewigrvYmHWbDrq2uvvPB1AmTWKuM+dI1JXQSUu2pIrYLb/QncyiIGkFDFKTwJ0XqQZZg==}
engines: {node: ^18.0.0 || >=20}
peerDependencies:
@ -1197,28 +1197,28 @@ packages:
svelte: ^4.0.0 || ^5.0.0-next.0
vite: ^5.0.0
dependencies:
'@sveltejs/vite-plugin-svelte': 3.0.2(svelte@5.0.0-next.44)(vite@5.0.12)
'@sveltejs/vite-plugin-svelte': 3.0.2(svelte@5.0.0-next.45)(vite@5.0.12)
debug: 4.3.4
svelte: 5.0.0-next.44
svelte: 5.0.0-next.45
vite: 5.0.12(sass@1.70.0)
transitivePeerDependencies:
- supports-color
dev: false
/@sveltejs/vite-plugin-svelte@3.0.2(svelte@5.0.0-next.44)(vite@5.0.12):
/@sveltejs/vite-plugin-svelte@3.0.2(svelte@5.0.0-next.45)(vite@5.0.12):
resolution: {integrity: sha512-MpmF/cju2HqUls50WyTHQBZUV3ovV/Uk8k66AN2gwHogNAG8wnW8xtZDhzNBsFJJuvmq1qnzA5kE7YfMJNFv2Q==}
engines: {node: ^18.0.0 || >=20}
peerDependencies:
svelte: ^4.0.0 || ^5.0.0-next.0
vite: ^5.0.0
dependencies:
'@sveltejs/vite-plugin-svelte-inspector': 2.0.0(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@5.0.0-next.44)(vite@5.0.12)
'@sveltejs/vite-plugin-svelte-inspector': 2.0.0(@sveltejs/vite-plugin-svelte@3.0.2)(svelte@5.0.0-next.45)(vite@5.0.12)
debug: 4.3.4
deepmerge: 4.3.1
kleur: 4.1.5
magic-string: 0.30.6
svelte: 5.0.0-next.44
svelte-hmr: 0.15.3(svelte@5.0.0-next.44)
svelte: 5.0.0-next.45
svelte-hmr: 0.15.3(svelte@5.0.0-next.45)
vite: 5.0.12(sass@1.70.0)
vitefu: 0.2.5(vite@5.0.12)
transitivePeerDependencies:
@ -1423,19 +1423,19 @@ packages:
hasBin: true
dev: false
/astro-pagefind@1.4.0(astro@4.3.0):
/astro-pagefind@1.4.0(astro@4.3.2):
resolution: {integrity: sha512-WMUlS0TTqIDqygplj/cxKU+l3oI+zB2qgYNMZed5R5nXuw8HhAKATog9zazuN05TOSWxI2Ap9+9WAYnnJ+4C7A==}
peerDependencies:
astro: ^2.0.4 || ^3.0.0 || ^4.0.0
dependencies:
'@pagefind/default-ui': 1.0.4
astro: 4.3.0(sass@1.70.0)(typescript@5.3.3)
astro: 4.3.2(sass@1.70.0)(typescript@5.3.3)
pagefind: 1.0.4
sirv: 2.0.4
dev: false
/astro@4.3.0(sass@1.70.0)(typescript@5.3.3):
resolution: {integrity: sha512-DSn7q7KJ9lmA7B3faz6uaIIRTzJduKNYRAsgXDn+wH1Jso33PFOU3ZcI4knlMAqhFW3MX8WorB8Hg6kR+waQXg==}
/astro@4.3.2(sass@1.70.0)(typescript@5.3.3):
resolution: {integrity: sha512-SJotHzKG/I32ruYWItMRJjtmNQh14mVS7kahu3XfYyxvo6nx08PGJBTSPbqPrW1sjeQPHwcpVDFx3yMMK2kaxQ==}
engines: {node: '>=18.14.1', npm: '>=6.14.0'}
hasBin: true
dependencies:
@ -1588,8 +1588,8 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
dependencies:
caniuse-lite: 1.0.30001582
electron-to-chromium: 1.4.653
caniuse-lite: 1.0.30001583
electron-to-chromium: 1.4.656
node-releases: 2.0.14
update-browserslist-db: 1.0.13(browserslist@4.22.3)
dev: false
@ -1613,8 +1613,8 @@ packages:
engines: {node: '>=14.16'}
dev: false
/caniuse-lite@1.0.30001582:
resolution: {integrity: sha512-vsJG3V5vgfduaQGVxL53uSX/HUzxyr2eA8xCo36OLal7sRcSZbibJtLeh0qja4sFOr/QQGt4opB4tOy+eOgAxg==}
/caniuse-lite@1.0.30001583:
resolution: {integrity: sha512-acWTYaha8xfhA/Du/z4sNZjHUWjkiuoAi2LM+T/aL+kemKQgPT1xBb/YKjlQ0Qo8gvbHsGNplrEJ+9G3gL7i4Q==}
dev: false
/ccount@2.0.1:
@ -1926,8 +1926,8 @@ packages:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
dev: false
/electron-to-chromium@1.4.653:
resolution: {integrity: sha512-wA2A2LQCqnEwQAvwADQq3KpMpNwgAUBnRmrFgRzHnPhbQUFArTR32Ab46f4p0MovDLcg4uqd4nCsN2hTltslpA==}
/electron-to-chromium@1.4.656:
resolution: {integrity: sha512-9AQB5eFTHyR3Gvt2t/NwR0le2jBSUNwCnMbUCejFWHD+so4tH40/dRLgoE+jxlPeWS43XJewyvCv+I8LPMl49Q==}
dev: false
/emoji-regex@10.3.0:
@ -4240,8 +4240,8 @@ packages:
- supports-color
dev: false
/streamx@2.15.6:
resolution: {integrity: sha512-q+vQL4AAz+FdfT137VF69Cc/APqUbxy+MDOImRrMvchJpigHj9GksgDU2LYbO9rx7RX6osWgxJB2WxhYv4SZAw==}
/streamx@2.15.7:
resolution: {integrity: sha512-NPEKS5+yjyo597eafGbKW5ujh7Sm6lDLHZQd/lRSz6S0VarpADBJItqfB4PnwpS+472oob1GX5cCY9vzfJpHUA==}
requiresBuild: true
dependencies:
fast-fifo: 1.3.2
@ -4358,16 +4358,16 @@ packages:
engines: {node: '>= 0.4'}
dev: false
/svelte-hmr@0.15.3(svelte@5.0.0-next.44):
/svelte-hmr@0.15.3(svelte@5.0.0-next.45):
resolution: {integrity: sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==}
engines: {node: ^12.20 || ^14.13.1 || >= 16}
peerDependencies:
svelte: ^3.19.0 || ^4.0.0
dependencies:
svelte: 5.0.0-next.44
svelte: 5.0.0-next.45
dev: false
/svelte2tsx@0.6.27(svelte@5.0.0-next.44)(typescript@5.3.3):
/svelte2tsx@0.6.27(svelte@5.0.0-next.45)(typescript@5.3.3):
resolution: {integrity: sha512-E1uPW1o6VsbRz+nUk3fznZ2lSmCITAJoNu8AYefWSvIwE2pSB01i5sId4RMbWNzfcwCQl1DcgGShCPcldl4rvg==}
peerDependencies:
svelte: ^3.55 || ^4.0.0-next.0 || ^4.0 || ^5.0.0-next.0
@ -4375,12 +4375,12 @@ packages:
dependencies:
dedent-js: 1.0.1
pascal-case: 3.1.2
svelte: 5.0.0-next.44
svelte: 5.0.0-next.45
typescript: 5.3.3
dev: false
/svelte@5.0.0-next.44:
resolution: {integrity: sha512-VO0HOOdCOURQgZ+KodijZGXQe6zp9U53ZmQlm1Lmy7/5p7QtFUHb420eM/KHaR9g8F1uFPK4xIlRBx3q5J0t3g==}
/svelte@5.0.0-next.45:
resolution: {integrity: sha512-+Cy7+SzAOrtunlKxkPg7cpJiEuc2TM5TJTAU50OI1PkiCjYmWLvvxZYwn0ulHMRJL6vQ4CCOOMUGbs7kUoqBKg==}
engines: {node: '>=18'}
dependencies:
'@ampproject/remapping': 2.2.1
@ -4448,7 +4448,7 @@ packages:
dependencies:
b4a: 1.6.4
fast-fifo: 1.3.2
streamx: 2.15.6
streamx: 2.15.7
dev: false
optional: true

View file

@ -0,0 +1,9 @@
---
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>

View file

@ -0,0 +1,32 @@
export interface KKLCEntry {
entry: number;
char: string;
meanings: string[];
on: string[];
kun: string[];
examples: Array<
[Array<[string, string]>, string]
>;
senses: string[];
}
async function chooseKanji(): Promise<number> {
const date = new Date().toLocaleDateString('en');
console.log(date);
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;
const id = min + (hashValue % (max - min + 1));
return id;
}
export async function fetchKanji(): Promise<KKLCEntry> {
return await fetch(`/static/kanji/${await chooseKanji()}.json`).then(res => res.json());
}

View file

@ -0,0 +1,49 @@
<script lang="ts">
import { fetchKanji, type KKLCEntry } from './data.svelte.ts';
let state = $state<Promise<KKLCEntry>>(new Promise(() => {}));
$effect(() => void (state = fetchKanji()));
</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.meanings.join(', ')}
</div>
<div class="info-on">
{state.on.join(', ')}
</div>
<div class="info-kun">
{state.kun.join(', ')}
</div>
</div>
</div>
<table>
<tbody>
{#each state.examples as [example, meaning]}
<tr>
<td>
<ruby>
{#each example as [kanji, furigana]}{kanji}<rt>{furigana || ''}</rt>{/each}
</ruby>
</td>
<td>
{meaning}
</td>
</tr>
{/each}
</tbody>
</table>
{/await}
</div>

View file

@ -1,6 +1,7 @@
---
import Base from "@layouts/Base.astro";
import Intro from "@components/cards/Intro.astro";
import Kanji from "@components/cards/kanji/Kanji.astro";
import Photo from "@components/cards/Photo.astro";
import Text from "@assets/markdown/index.md";
---
@ -11,6 +12,7 @@ import Text from "@assets/markdown/index.md";
</article>
<aside class="l-home__aside">
<Intro/>
<Kanji/>
<Photo/>
</aside>
</main>

View file

@ -21,7 +21,7 @@ main {
--fs-xs: 0.833rem;
--fs-sm: 1rem;
--fs-md: 1.2rem;
--fs-lg: 1.44rem;
--fs-lg: 1.44rem;
--fs-xl: 1.728rem;
}
}

View file

@ -0,0 +1,102 @@
@use 'sass:math';
.daily-kanji {
.info {
display: flex;
justify-content: flex-start;
margin-bottom: 0.5em;
gap: 1em;
}
.info-key {
font-weight: 700;
}
.info-char {
position: relative;
padding: 0.1em;
border: 1px dashed black;
font-family: serif;
font-size: 5rem;
line-height: 1em;
&::before {
top: -0.01em;
left: 0.6em;
width: 0;
height: 100%;
border-right: 1px dashed #000;
}
&::after {
top: 0.6em;
left: -0.01em;
width: 100%;
height: 0;
border-top: 1px dashed #000;
}
&::before, &::after {
content: '';
position: absolute;
display: block;
}
}
.ruby-cell {
min-width: 5em;
}
$side: 5em;
$radius: $side / 2;
.spinner-wrap {
display: flex;
justify-content: center;
margin-top: 1em;
}
.spinner {
display: inline-block;
position: relative;
width: $side;
height: $side;
div {
animation: loading 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
transform-origin: $radius $radius;
&::after {
content: '';
display: block;
position: absolute;
width: 7px;
height: 7px;
border-radius: 50%;
background: #000;
margin: -4px 0 0 -4px;
}
@for $i from 1 through 8 {
$angle: 15deg * $i;
&:nth-child(#{$i}) {
animation-delay: -0.036s * $i;
&::after {
top: $radius + math.sin($angle) * $radius;
left: $radius + math.cos($angle) * $radius;
}
}
}
}
}
@keyframes loading {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
}

View file

@ -10,6 +10,7 @@
@use 'components/search';
@use 'components/link-tree';
@use 'components/bibliography';
@use 'components/kanji';
// Partials
@use 'partials/navbar';