refactor!: remove react dependency for UI interactions (#457)

Remove react as a dependency for UI interactions.
Update jsx components such as Search and Datetime
with astro components.
This commit is contained in:
Sat Naing
2025-03-02 21:05:36 +07:00
parent 846315a265
commit 35429176e0
20 changed files with 467 additions and 925 deletions
-2
View File
@@ -1,6 +1,5 @@
import { defineConfig } from "astro/config";
import tailwindcss from "@tailwindcss/vite";
import react from "@astrojs/react";
import sitemap from "@astrojs/sitemap";
import remarkToc from "remark-toc";
import remarkCollapse from "remark-collapse";
@@ -10,7 +9,6 @@ import { SITE } from "./src/config";
export default defineConfig({
site: SITE.website,
integrations: [
react(),
sitemap({
filter: page => SITE.showArchives || !page.endsWith("/archives"),
}),
-5
View File
@@ -12,18 +12,13 @@
"lint": "eslint ."
},
"dependencies": {
"@astrojs/react": "^4.2.0",
"@astrojs/rss": "^4.0.11",
"@astrojs/sitemap": "^3.2.1",
"@resvg/resvg-js": "^2.6.2",
"@tailwindcss/vite": "^4.0.8",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"astro": "^5.3.1",
"fuse.js": "^7.1.0",
"lodash.kebabcase": "^4.1.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"remark-collapse": "^0.1.2",
"remark-toc": "^9.0.0",
"satori": "^0.12.1",
-436
View File
@@ -8,9 +8,6 @@ importers:
.:
dependencies:
'@astrojs/react':
specifier: ^4.2.0
version: 4.2.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(jiti@2.4.2)(lightningcss@1.29.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(yaml@2.7.0)
'@astrojs/rss':
specifier: ^4.0.11
version: 4.0.11
@@ -23,12 +20,6 @@ importers:
'@tailwindcss/vite':
specifier: ^4.0.8
version: 4.0.8(vite@6.1.0(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0))
'@types/react':
specifier: ^19.0.10
version: 19.0.10
'@types/react-dom':
specifier: ^19.0.4
version: 19.0.4(@types/react@19.0.10)
astro:
specifier: ^5.3.1
version: 5.3.1(jiti@2.4.2)(lightningcss@1.29.1)(rollup@4.34.7)(typescript@5.7.3)(yaml@2.7.0)
@@ -38,12 +29,6 @@ importers:
lodash.kebabcase:
specifier: ^4.1.1
version: 4.1.1
react:
specifier: ^19.0.0
version: 19.0.0
react-dom:
specifier: ^19.0.0
version: 19.0.0(react@19.0.0)
remark-collapse:
specifier: ^0.1.2
version: 0.1.2
@@ -99,10 +84,6 @@ importers:
packages:
'@ampproject/remapping@2.3.0':
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
'@astrojs/check@0.9.4':
resolution: {integrity: sha512-IOheHwCtpUfvogHHsvu0AbeRZEnjJg3MopdLddkJE70mULItS/Vh37BHcI00mcOJcH1vhD3odbpvWokpxam7xA==}
hasBin: true
@@ -134,15 +115,6 @@ packages:
resolution: {integrity: sha512-GilTHKGCW6HMq7y3BUv9Ac7GMe/MO9gi9GW62GzKtth0SwukCu/qp2wLiGpEujhY+VVhaG9v7kv/5vFzvf4NYw==}
engines: {node: ^18.17.1 || ^20.3.0 || >=22.0.0}
'@astrojs/react@4.2.0':
resolution: {integrity: sha512-2OccnYFK+mLuy9GpJqPM3BQGvvemnXNeww+nBVYFuiH04L7YIdfg4Gq0LT7v/BraiuADV5uTl9VhTDL/ZQPAhw==}
engines: {node: ^18.17.1 || ^20.3.0 || >=22.0.0}
peerDependencies:
'@types/react': ^17.0.50 || ^18.0.21 || ^19.0.0
'@types/react-dom': ^17.0.17 || ^18.0.6 || ^19.0.0
react: ^17.0.2 || ^18.0.0 || ^19.0.0
react-dom: ^17.0.2 || ^18.0.0 || ^19.0.0
'@astrojs/rss@4.0.11':
resolution: {integrity: sha512-3e3H8i6kc97KGnn9iaZBJpIkdoQi8MmR5zH5R+dWsfCM44lLTszOqy1OBfGGxDt56mpQkYVtZJWoxMyWuUZBfw==}
@@ -156,40 +128,6 @@ packages:
'@astrojs/yaml2ts@0.2.2':
resolution: {integrity: sha512-GOfvSr5Nqy2z5XiwqTouBBpy5FyI6DEe+/g/Mk5am9SjILN1S5fOEvYK0GuWHg98yS/dobP4m8qyqw/URW35fQ==}
'@babel/code-frame@7.26.2':
resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
engines: {node: '>=6.9.0'}
'@babel/compat-data@7.26.8':
resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==}
engines: {node: '>=6.9.0'}
'@babel/core@7.26.9':
resolution: {integrity: sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==}
engines: {node: '>=6.9.0'}
'@babel/generator@7.26.9':
resolution: {integrity: sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==}
engines: {node: '>=6.9.0'}
'@babel/helper-compilation-targets@7.26.5':
resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==}
engines: {node: '>=6.9.0'}
'@babel/helper-module-imports@7.25.9':
resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==}
engines: {node: '>=6.9.0'}
'@babel/helper-module-transforms@7.26.0':
resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
'@babel/helper-plugin-utils@7.26.5':
resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==}
engines: {node: '>=6.9.0'}
'@babel/helper-string-parser@7.25.9':
resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==}
engines: {node: '>=6.9.0'}
@@ -198,39 +136,11 @@ packages:
resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
engines: {node: '>=6.9.0'}
'@babel/helper-validator-option@7.25.9':
resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==}
engines: {node: '>=6.9.0'}
'@babel/helpers@7.26.9':
resolution: {integrity: sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==}
engines: {node: '>=6.9.0'}
'@babel/parser@7.26.9':
resolution: {integrity: sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==}
engines: {node: '>=6.0.0'}
hasBin: true
'@babel/plugin-transform-react-jsx-self@7.25.9':
resolution: {integrity: sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/plugin-transform-react-jsx-source@7.25.9':
resolution: {integrity: sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/template@7.26.9':
resolution: {integrity: sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==}
engines: {node: '>=6.9.0'}
'@babel/traverse@7.26.9':
resolution: {integrity: sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==}
engines: {node: '>=6.9.0'}
'@babel/types@7.26.9':
resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==}
engines: {node: '>=6.9.0'}
@@ -568,24 +478,9 @@ packages:
cpu: [x64]
os: [win32]
'@jridgewell/gen-mapping@0.3.8':
resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==}
engines: {node: '>=6.0.0'}
'@jridgewell/resolve-uri@3.1.2':
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
engines: {node: '>=6.0.0'}
'@jridgewell/set-array@1.2.1':
resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
engines: {node: '>=6.0.0'}
'@jridgewell/sourcemap-codec@1.5.0':
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
'@jridgewell/trace-mapping@0.3.25':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -894,18 +789,6 @@ packages:
peerDependencies:
vite: ^5.2.0 || ^6
'@types/babel__core@7.20.5':
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
'@types/babel__generator@7.6.8':
resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==}
'@types/babel__template@7.4.4':
resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
'@types/babel__traverse@7.20.6':
resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==}
'@types/cookie@0.6.0':
resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
@@ -939,14 +822,6 @@ packages:
'@types/node@17.0.45':
resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==}
'@types/react-dom@19.0.4':
resolution: {integrity: sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==}
peerDependencies:
'@types/react': ^19.0.0
'@types/react@19.0.10':
resolution: {integrity: sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==}
'@types/sax@1.2.7':
resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==}
@@ -1018,12 +893,6 @@ packages:
'@ungap/structured-clone@1.3.0':
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
'@vitejs/plugin-react@4.3.4':
resolution: {integrity: sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
vite: ^4.2.0 || ^5.0.0 || ^6.0.0
'@volar/kit@2.4.11':
resolution: {integrity: sha512-ups5RKbMzMCr6RKafcCqDRnJhJDNWqo2vfekwOAj6psZ15v5TlcQFQAyokQJ3wZxVkzxrQM+TqTRDENfQEXpmA==}
peerDependencies:
@@ -1155,11 +1024,6 @@ packages:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
browserslist@4.24.4:
resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
callsites@3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
@@ -1171,9 +1035,6 @@ packages:
camelize@1.0.1:
resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}
caniuse-lite@1.0.30001700:
resolution: {integrity: sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==}
ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
@@ -1241,9 +1102,6 @@ packages:
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
cookie-es@1.2.2:
resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==}
@@ -1280,9 +1138,6 @@ packages:
engines: {node: '>=4'}
hasBin: true
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
debug@4.4.0:
resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
engines: {node: '>=6.0'}
@@ -1338,9 +1193,6 @@ packages:
resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==}
engines: {node: '>=4'}
electron-to-chromium@1.5.102:
resolution: {integrity: sha512-eHhqaja8tE/FNpIiBrvBjFV/SSKpyWHLvxuR9dPTdo+3V9ppdLmFB7ZZQ98qNovcngPLYIz0oOBF9P0FfZef5Q==}
emmet@2.4.11:
resolution: {integrity: sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==}
@@ -1536,10 +1388,6 @@ packages:
resolution: {integrity: sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==}
engines: {node: '>=10'}
gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'}
get-caller-file@2.0.5:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
@@ -1559,10 +1407,6 @@ packages:
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
engines: {node: '>=10.13.0'}
globals@11.12.0:
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
engines: {node: '>=4'}
globals@14.0.0:
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
engines: {node: '>=18'}
@@ -1697,9 +1541,6 @@ packages:
resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
hasBin: true
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
js-yaml@3.14.1:
resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
hasBin: true
@@ -1708,11 +1549,6 @@ packages:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
hasBin: true
jsesc@3.1.0:
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
engines: {node: '>=6'}
hasBin: true
json-buffer@3.0.1:
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
@@ -1725,11 +1561,6 @@ packages:
json-stable-stringify-without-jsonify@1.0.1:
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
json5@2.2.3:
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
engines: {node: '>=6'}
hasBin: true
jsonc-parser@2.3.1:
resolution: {integrity: sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==}
@@ -1851,9 +1682,6 @@ packages:
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
magic-string@0.30.17:
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
@@ -2041,9 +1869,6 @@ packages:
node-mock-http@1.0.0:
resolution: {integrity: sha512-0uGYQ1WQL1M5kKvGRXWQ3uZCHtLTO8hln3oBjIusM75WoesZ909uQJs/Hb946i2SS+Gsrhkaa6iAO17jRIv6DQ==}
node-releases@2.0.19:
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
@@ -2252,19 +2077,6 @@ packages:
radix3@1.1.2:
resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==}
react-dom@19.0.0:
resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==}
peerDependencies:
react: ^19.0.0
react-refresh@0.14.2:
resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
engines: {node: '>=0.10.0'}
react@19.0.0:
resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==}
engines: {node: '>=0.10.0'}
readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
@@ -2371,13 +2183,6 @@ packages:
sax@1.4.1:
resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
scheduler@0.25.0:
resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==}
semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
semver@7.7.1:
resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==}
engines: {node: '>=10'}
@@ -2643,12 +2448,6 @@ packages:
uploadthing:
optional: true
update-browserslist-db@1.1.2:
resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==}
hasBin: true
peerDependencies:
browserslist: '>= 4.21.0'
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
@@ -2857,9 +2656,6 @@ packages:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
yaml-language-server@1.15.0:
resolution: {integrity: sha512-N47AqBDCMQmh6mBLmI6oqxryHRzi33aPFPsJhYy3VTUGCdLHYjGh4FZzpUjRlphaADBBkDmnkM/++KNIOHi5Rw==}
hasBin: true
@@ -2919,11 +2715,6 @@ packages:
snapshots:
'@ampproject/remapping@2.3.0':
dependencies:
'@jridgewell/gen-mapping': 0.3.8
'@jridgewell/trace-mapping': 0.3.25
'@astrojs/check@0.9.4(prettier-plugin-astro@0.14.1)(prettier@3.5.2)(typescript@5.7.3)':
dependencies:
'@astrojs/language-server': 2.15.4(prettier-plugin-astro@0.14.1)(prettier@3.5.2)(typescript@5.7.3)
@@ -2994,29 +2785,6 @@ snapshots:
dependencies:
prismjs: 1.29.0
'@astrojs/react@4.2.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(jiti@2.4.2)(lightningcss@1.29.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(yaml@2.7.0)':
dependencies:
'@types/react': 19.0.10
'@types/react-dom': 19.0.4(@types/react@19.0.10)
'@vitejs/plugin-react': 4.3.4(vite@6.1.0(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0))
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
ultrahtml: 1.5.3
vite: 6.1.0(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0)
transitivePeerDependencies:
- '@types/node'
- jiti
- less
- lightningcss
- sass
- sass-embedded
- stylus
- sugarss
- supports-color
- terser
- tsx
- yaml
'@astrojs/rss@4.0.11':
dependencies:
fast-xml-parser: 4.5.3
@@ -3044,111 +2812,14 @@ snapshots:
dependencies:
yaml: 2.7.0
'@babel/code-frame@7.26.2':
dependencies:
'@babel/helper-validator-identifier': 7.25.9
js-tokens: 4.0.0
picocolors: 1.1.1
'@babel/compat-data@7.26.8': {}
'@babel/core@7.26.9':
dependencies:
'@ampproject/remapping': 2.3.0
'@babel/code-frame': 7.26.2
'@babel/generator': 7.26.9
'@babel/helper-compilation-targets': 7.26.5
'@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.9)
'@babel/helpers': 7.26.9
'@babel/parser': 7.26.9
'@babel/template': 7.26.9
'@babel/traverse': 7.26.9
'@babel/types': 7.26.9
convert-source-map: 2.0.0
debug: 4.4.0
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
transitivePeerDependencies:
- supports-color
'@babel/generator@7.26.9':
dependencies:
'@babel/parser': 7.26.9
'@babel/types': 7.26.9
'@jridgewell/gen-mapping': 0.3.8
'@jridgewell/trace-mapping': 0.3.25
jsesc: 3.1.0
'@babel/helper-compilation-targets@7.26.5':
dependencies:
'@babel/compat-data': 7.26.8
'@babel/helper-validator-option': 7.25.9
browserslist: 4.24.4
lru-cache: 5.1.1
semver: 6.3.1
'@babel/helper-module-imports@7.25.9':
dependencies:
'@babel/traverse': 7.26.9
'@babel/types': 7.26.9
transitivePeerDependencies:
- supports-color
'@babel/helper-module-transforms@7.26.0(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
'@babel/helper-module-imports': 7.25.9
'@babel/helper-validator-identifier': 7.25.9
'@babel/traverse': 7.26.9
transitivePeerDependencies:
- supports-color
'@babel/helper-plugin-utils@7.26.5': {}
'@babel/helper-string-parser@7.25.9': {}
'@babel/helper-validator-identifier@7.25.9': {}
'@babel/helper-validator-option@7.25.9': {}
'@babel/helpers@7.26.9':
dependencies:
'@babel/template': 7.26.9
'@babel/types': 7.26.9
'@babel/parser@7.26.9':
dependencies:
'@babel/types': 7.26.9
'@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
'@babel/helper-plugin-utils': 7.26.5
'@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
'@babel/helper-plugin-utils': 7.26.5
'@babel/template@7.26.9':
dependencies:
'@babel/code-frame': 7.26.2
'@babel/parser': 7.26.9
'@babel/types': 7.26.9
'@babel/traverse@7.26.9':
dependencies:
'@babel/code-frame': 7.26.2
'@babel/generator': 7.26.9
'@babel/parser': 7.26.9
'@babel/template': 7.26.9
'@babel/types': 7.26.9
debug: 4.4.0
globals: 11.12.0
transitivePeerDependencies:
- supports-color
'@babel/types@7.26.9':
dependencies:
'@babel/helper-string-parser': 7.25.9
@@ -3387,23 +3058,8 @@ snapshots:
'@img/sharp-win32-x64@0.33.5':
optional: true
'@jridgewell/gen-mapping@0.3.8':
dependencies:
'@jridgewell/set-array': 1.2.1
'@jridgewell/sourcemap-codec': 1.5.0
'@jridgewell/trace-mapping': 0.3.25
'@jridgewell/resolve-uri@3.1.2': {}
'@jridgewell/set-array@1.2.1': {}
'@jridgewell/sourcemap-codec@1.5.0': {}
'@jridgewell/trace-mapping@0.3.25':
dependencies:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.0
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -3645,27 +3301,6 @@ snapshots:
tailwindcss: 4.0.8
vite: 6.1.0(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0)
'@types/babel__core@7.20.5':
dependencies:
'@babel/parser': 7.26.9
'@babel/types': 7.26.9
'@types/babel__generator': 7.6.8
'@types/babel__template': 7.4.4
'@types/babel__traverse': 7.20.6
'@types/babel__generator@7.6.8':
dependencies:
'@babel/types': 7.26.9
'@types/babel__template@7.4.4':
dependencies:
'@babel/parser': 7.26.9
'@babel/types': 7.26.9
'@types/babel__traverse@7.20.6':
dependencies:
'@babel/types': 7.26.9
'@types/cookie@0.6.0': {}
'@types/debug@4.1.12':
@@ -3698,14 +3333,6 @@ snapshots:
'@types/node@17.0.45': {}
'@types/react-dom@19.0.4(@types/react@19.0.10)':
dependencies:
'@types/react': 19.0.10
'@types/react@19.0.10':
dependencies:
csstype: 3.1.3
'@types/sax@1.2.7':
dependencies:
'@types/node': 17.0.45
@@ -3805,17 +3432,6 @@ snapshots:
'@ungap/structured-clone@1.3.0': {}
'@vitejs/plugin-react@4.3.4(vite@6.1.0(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0))':
dependencies:
'@babel/core': 7.26.9
'@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.9)
'@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.9)
'@types/babel__core': 7.20.5
react-refresh: 0.14.2
vite: 6.1.0(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0)
transitivePeerDependencies:
- supports-color
'@volar/kit@2.4.11(typescript@5.7.3)':
dependencies:
'@volar/language-service': 2.4.11
@@ -4072,21 +3688,12 @@ snapshots:
dependencies:
fill-range: 7.1.1
browserslist@4.24.4:
dependencies:
caniuse-lite: 1.0.30001700
electron-to-chromium: 1.5.102
node-releases: 2.0.19
update-browserslist-db: 1.1.2(browserslist@4.24.4)
callsites@3.1.0: {}
camelcase@8.0.0: {}
camelize@1.0.1: {}
caniuse-lite@1.0.30001700: {}
ccount@2.0.1: {}
chalk@4.1.2:
@@ -4152,8 +3759,6 @@ snapshots:
concat-map@0.0.1: {}
convert-source-map@2.0.0: {}
cookie-es@1.2.2: {}
cookie@0.7.2: {}
@@ -4184,8 +3789,6 @@ snapshots:
cssesc@3.0.0: {}
csstype@3.1.3: {}
debug@4.4.0:
dependencies:
ms: 2.1.3
@@ -4222,8 +3825,6 @@ snapshots:
dset@3.1.4: {}
electron-to-chromium@1.5.102: {}
emmet@2.4.11:
dependencies:
'@emmetio/abbreviation': 2.3.3
@@ -4450,8 +4051,6 @@ snapshots:
fuse.js@7.1.0: {}
gensync@1.0.0-beta.2: {}
get-caller-file@2.0.5: {}
get-east-asian-width@1.3.0: {}
@@ -4466,8 +4065,6 @@ snapshots:
dependencies:
is-glob: 4.0.3
globals@11.12.0: {}
globals@14.0.0: {}
globals@15.15.0: {}
@@ -4633,8 +4230,6 @@ snapshots:
jiti@2.4.2: {}
js-tokens@4.0.0: {}
js-yaml@3.14.1:
dependencies:
argparse: 1.0.10
@@ -4644,8 +4239,6 @@ snapshots:
dependencies:
argparse: 2.0.1
jsesc@3.1.0: {}
json-buffer@3.0.1: {}
json-schema-traverse@0.4.1: {}
@@ -4654,8 +4247,6 @@ snapshots:
json-stable-stringify-without-jsonify@1.0.1: {}
json5@2.2.3: {}
jsonc-parser@2.3.1: {}
jsonc-parser@3.3.1: {}
@@ -4752,10 +4343,6 @@ snapshots:
lru-cache@10.4.3: {}
lru-cache@5.1.1:
dependencies:
yallist: 3.1.1
magic-string@0.30.17:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
@@ -5130,8 +4717,6 @@ snapshots:
node-mock-http@1.0.0: {}
node-releases@2.0.19: {}
normalize-path@3.0.0: {}
ofetch@1.4.1:
@@ -5286,15 +4871,6 @@ snapshots:
radix3@1.1.2: {}
react-dom@19.0.0(react@19.0.0):
dependencies:
react: 19.0.0
scheduler: 0.25.0
react-refresh@0.14.2: {}
react@19.0.0: {}
readdirp@3.6.0:
dependencies:
picomatch: 2.3.1
@@ -5476,10 +5052,6 @@ snapshots:
sax@1.4.1: {}
scheduler@0.25.0: {}
semver@6.3.1: {}
semver@7.7.1: {}
sharp@0.33.5:
@@ -5723,12 +5295,6 @@ snapshots:
ofetch: 1.4.1
ufo: 1.5.4
update-browserslist-db@1.1.2(browserslist@4.24.4):
dependencies:
browserslist: 4.24.4
escalade: 3.2.0
picocolors: 1.1.1
uri-js@4.4.1:
dependencies:
punycode: 2.3.1
@@ -5909,8 +5475,6 @@ snapshots:
y18n@5.0.8: {}
yallist@3.1.1: {}
yaml-language-server@1.15.0:
dependencies:
ajv: 8.17.1
+37
View File
@@ -0,0 +1,37 @@
---
import { slugifyStr } from "@/utils/slugify";
import type { CollectionEntry } from "astro:content";
import Datetime from "./Datetime.astro";
export interface Props {
href?: string;
frontmatter: CollectionEntry<"blog">["data"];
secHeading?: boolean;
}
const { href, frontmatter, secHeading = true } = Astro.props;
const { title, pubDatetime, modDatetime, description } = frontmatter;
const headerProps = {
style: { viewTransitionName: slugifyStr(title) },
class: "text-lg font-medium decoration-dashed hover:underline",
};
---
<li class="my-6">
<a
href={href}
class="inline-block text-lg font-medium text-accent decoration-dashed underline-offset-4 focus-visible:no-underline focus-visible:underline-offset-0"
>
{
secHeading ? (
<h2 {...headerProps}>{title}</h2>
) : (
<h3 {...headerProps}>{title}</h3>
)
}
</a>
<Datetime pubDatetime={pubDatetime} modDatetime={modDatetime} />
<p>{description}</p>
</li>
-35
View File
@@ -1,35 +0,0 @@
import type { CollectionEntry } from "astro:content";
import { slugifyStr } from "@/utils/slugify";
import { Datetime } from "./Datetime";
export interface Props {
href?: string;
frontmatter: CollectionEntry<"blog">["data"];
secHeading?: boolean;
}
export default function Card({ href, frontmatter, secHeading = true }: Props) {
const { title, pubDatetime, modDatetime, description } = frontmatter;
const headerProps = {
style: { viewTransitionName: slugifyStr(title) },
className: "text-lg font-medium decoration-dashed hover:underline",
};
return (
<li className="my-6">
<a
href={href}
className="inline-block text-lg font-medium text-accent decoration-dashed underline-offset-4 focus-visible:no-underline focus-visible:underline-offset-0"
>
{secHeading ? (
<h2 {...headerProps}>{title}</h2>
) : (
<h3 {...headerProps}>{title}</h3>
)}
</a>
<Datetime pubDatetime={pubDatetime} modDatetime={modDatetime} />
<p>{description}</p>
</li>
);
}
+66
View File
@@ -0,0 +1,66 @@
---
import { LOCALE } from "@/constants";
export interface Props {
pubDatetime: string | Date;
modDatetime: string | Date | undefined | null;
size?: "sm" | "lg";
class?: string;
}
const {
pubDatetime,
modDatetime,
size = "sm",
class: className = "",
} = Astro.props;
/* ========== Formatted Datetime ========== */
const myDatetime = new Date(
modDatetime && modDatetime > pubDatetime ? modDatetime : pubDatetime
);
const date = myDatetime.toLocaleDateString(LOCALE.langTag, {
year: "numeric",
month: "short",
day: "numeric",
});
const time = myDatetime.toLocaleTimeString(LOCALE.langTag, {
hour: "2-digit",
minute: "2-digit",
});
---
<div class={`flex items-end space-x-2 opacity-80 ${className}`.trim()}>
<svg
xmlns="http://www.w3.org/2000/svg"
class={`${
size === "sm" ? "scale-90" : "scale-100"
} inline-block h-6 w-6 min-w-[1.375rem] fill-foreground`}
aria-hidden="true"
>
<path
d="M7 11h2v2H7zm0 4h2v2H7zm4-4h2v2h-2zm0 4h2v2h-2zm4-4h2v2h-2zm0 4h2v2h-2z"
></path>
<path
d="M5 22h14c1.103 0 2-.897 2-2V6c0-1.103-.897-2-2-2h-2V2h-2v2H9V2H7v2H5c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2zM19 8l.001 12H5V8h14z"
></path>
</svg>
{
modDatetime && modDatetime > pubDatetime ? (
<span
class={`italic ${size === "sm" ? "text-sm" : "text-sm sm:text-base"}`}
>
Updated:
</span>
) : (
<span class="sr-only">Published:</span>
)
}
<span class={`italic ${size === "sm" ? "text-sm" : "text-sm sm:text-base"}`}>
<time datetime={myDatetime.toISOString()}>{date}</time>
<span aria-hidden="true"> | </span>
<span class="sr-only">&nbsp;at&nbsp;</span>
<span class="text-nowrap">{time}</span>
</span>
</div>
-76
View File
@@ -1,76 +0,0 @@
import { LOCALE } from "@/constants";
interface DatetimesProps {
pubDatetime: string | Date;
modDatetime: string | Date | undefined | null;
}
interface Props extends DatetimesProps {
size?: "sm" | "lg";
className?: string;
}
export function Datetime({
pubDatetime,
modDatetime,
size = "sm",
className = "",
}: Props) {
return (
<div className={`flex items-end space-x-2 opacity-80 ${className}`.trim()}>
<svg
xmlns="http://www.w3.org/2000/svg"
className={`${
size === "sm" ? "scale-90" : "scale-100"
} inline-block h-6 w-6 min-w-[1.375rem] fill-foreground`}
aria-hidden="true"
>
<path d="M7 11h2v2H7zm0 4h2v2H7zm4-4h2v2h-2zm0 4h2v2h-2zm4-4h2v2h-2zm0 4h2v2h-2z"></path>
<path d="M5 22h14c1.103 0 2-.897 2-2V6c0-1.103-.897-2-2-2h-2V2h-2v2H9V2H7v2H5c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2zM19 8l.001 12H5V8h14z"></path>
</svg>
{modDatetime && modDatetime > pubDatetime ? (
<span
className={`italic ${size === "sm" ? "text-sm" : "text-sm sm:text-base"}`}
>
Updated:
</span>
) : (
<span className="sr-only">Published:</span>
)}
<span
className={`italic ${size === "sm" ? "text-sm" : "text-sm sm:text-base"}`}
>
<FormattedDatetime
pubDatetime={pubDatetime}
modDatetime={modDatetime}
/>
</span>
</div>
);
}
const FormattedDatetime = ({ pubDatetime, modDatetime }: DatetimesProps) => {
const myDatetime = new Date(
modDatetime && modDatetime > pubDatetime ? modDatetime : pubDatetime
);
const date = myDatetime.toLocaleDateString(LOCALE.langTag, {
year: "numeric",
month: "short",
day: "numeric",
});
const time = myDatetime.toLocaleTimeString(LOCALE.langTag, {
hour: "2-digit",
minute: "2-digit",
});
return (
<>
<time dateTime={myDatetime.toISOString()}>{date}</time>
<span aria-hidden="true"> | </span>
<span className="sr-only">&nbsp;at&nbsp;</span>
<span className="text-nowrap">{time}</span>
</>
);
};
-135
View File
@@ -1,135 +0,0 @@
import Fuse from "fuse.js";
import type { CollectionEntry } from "astro:content";
import { useEffect, useRef, useState, useMemo, type FormEvent } from "react";
import Card from "@/components/Card";
export type SearchItem = {
title: string;
description: string;
data: CollectionEntry<"blog">["data"];
slug: string;
};
interface Props {
searchList: SearchItem[];
backUrl: string;
}
interface SearchResult {
item: SearchItem;
refIndex: number;
}
export default function SearchBar({ searchList, backUrl }: Props) {
const inputRef = useRef<HTMLInputElement>(null);
const [inputVal, setInputVal] = useState("");
const [searchResults, setSearchResults] = useState<SearchResult[] | null>(
null
);
const handleChange = (e: FormEvent<HTMLInputElement>) => {
setInputVal(e.currentTarget.value);
};
const fuse = useMemo(
() =>
new Fuse(searchList, {
keys: ["title", "description"],
includeMatches: true,
minMatchCharLength: 2,
threshold: 0.5,
}),
[searchList]
);
useEffect(() => {
// if URL has search query,
// insert that search query in input field
const searchUrl = new URLSearchParams(window.location.search);
const searchStr = searchUrl.get("q");
if (searchStr) setInputVal(searchStr);
// put focus cursor at the end of the string
setTimeout(function () {
inputRef.current!.selectionStart = inputRef.current!.selectionEnd =
searchStr?.length || 0;
}, 50);
}, []);
let newRelativePathQuery = "";
useEffect(() => {
// Add search result only if
// input value is more than one character
const inputResult = inputVal.length > 1 ? fuse.search(inputVal) : [];
setSearchResults(inputResult);
// Update search string in URL
if (inputVal.length > 0) {
const searchParams = new URLSearchParams(window.location.search);
searchParams.set("q", inputVal);
newRelativePathQuery =
window.location.pathname + "?" + searchParams.toString();
history.replaceState(history.state, "", newRelativePathQuery);
} else {
history.replaceState(history.state, "", window.location.pathname);
}
}, [inputVal]);
useEffect(() => {
// focus on text input when search bar is displayed
if (inputRef.current) {
inputRef.current.focus();
}
}, [inputVal]);
const fullBackUrl = `${backUrl}${inputVal.trim() !== "" ? `?q=${inputVal}` : "/"}`;
return (
<>
<label className="relative block">
<span className="sr-only">Search</span>
<span className="absolute inset-y-0 left-0 flex items-center pl-2 opacity-75">
<svg
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
className="inline-block size-6 fill-foreground"
>
<path d="M19.023 16.977a35.13 35.13 0 0 1-1.367-1.384c-.372-.378-.596-.653-.596-.653l-2.8-1.337A6.962 6.962 0 0 0 16 9c0-3.859-3.14-7-7-7S2 5.141 2 9s3.14 7 7 7c1.763 0 3.37-.66 4.603-1.739l1.337 2.8s.275.224.653.596c.387.363.896.854 1.384 1.367l1.358 1.392.604.646 2.121-2.121-.646-.604c-.379-.372-.885-.866-1.391-1.36zM9 14c-2.757 0-5-2.243-5-5s2.243-5 5-5 5 2.243 5 5-2.243 5-5 5z"></path>
</svg>
</span>
<input
className="border-skin-fill/40 bg-skin-fill focus:border-skin-accent block w-full rounded border py-3 pr-3 pl-10 placeholder:italic focus:outline-none"
placeholder="Search for anything..."
type="text"
name="search"
value={inputVal}
onChange={handleChange}
autoComplete="off"
// autoFocus
ref={inputRef}
/>
</label>
{inputVal.length > 1 && (
<div className="mt-8">
Found {searchResults?.length}
{searchResults?.length && searchResults?.length === 1
? " result"
: " results"}{" "}
for '{inputVal}'
</div>
)}
<ul>
{searchResults &&
searchResults.map(({ item, refIndex }) => (
<Card
href={`/posts/${item.slug}${fullBackUrl}`}
frontmatter={item.data}
key={`${refIndex}-${item.slug}`}
/>
))}
</ul>
</>
);
}
+2 -2
View File
@@ -4,7 +4,7 @@ import Layout from "@/layouts/Layout.astro";
import Header from "@/components/Header.astro";
import Footer from "@/components/Footer.astro";
import Tag from "@/components/Tag.astro";
import { Datetime } from "@/components/Datetime";
import Datetime from "@/components/Datetime.astro";
import EditPost from "@/components/EditPost.astro";
import ShareLinks from "@/components/ShareLinks.astro";
import BackButton from "@/components/BackButton.astro";
@@ -86,7 +86,7 @@ const nextPost =
pubDatetime={pubDatetime}
modDatetime={modDatetime}
size="lg"
className="my-2"
class="my-2"
/>
<EditPost class="max-sm:hidden" editPost={editPost} postId={post.id} />
</div>
+1 -1
View File
@@ -4,7 +4,7 @@ import Main from "@/layouts/Main.astro";
import Layout from "@/layouts/Layout.astro";
import Header from "@/components/Header.astro";
import Footer from "@/components/Footer.astro";
import Card from "@/components/Card";
import Card from "@/components/Card.astro";
import getPostsByGroupCondition from "@/utils/getPostsByGroupCondition";
import { SITE } from "@/config";
+1 -1
View File
@@ -5,7 +5,7 @@ import Header from "@/components/Header.astro";
import Footer from "@/components/Footer.astro";
import Socials from "@/components/Socials.astro";
import LinkButton from "@/components/LinkButton.astro";
import Card from "@/components/Card";
import Card from "@/components/Card.astro";
import Hr from "@/components/Hr.astro";
import getSortedPosts from "@/utils/getSortedPosts";
import IconRss from "@/assets/icons/IconRss.svg";
+1 -1
View File
@@ -5,7 +5,7 @@ import Main from "@/layouts/Main.astro";
import Layout from "@/layouts/Layout.astro";
import Header from "@/components/Header.astro";
import Footer from "@/components/Footer.astro";
import Card from "@/components/Card";
import Card from "@/components/Card.astro";
import Pagination from "@/components/Pagination.astro";
import getSortedPosts from "@/utils/getSortedPosts";
import { SITE } from "@/config";
+1 -18
View File
@@ -1,32 +1,15 @@
---
import { getCollection } from "astro:content";
import Main from "@/layouts/Main.astro";
import Layout from "@/layouts/Layout.astro";
import Header from "@/components/Header.astro";
import Footer from "@/components/Footer.astro";
import SearchBar from "@/components/SearchBar";
import getSortedPosts from "@/utils/getSortedPosts";
import { SITE } from "@/config";
// Retrieve all published articles
const posts = await getCollection("blog", ({ data }) => !data.draft);
const sortedPosts = getSortedPosts(posts);
// List of items to search in
const searchList = sortedPosts.map(({ data, id }) => ({
title: data.title,
description: data.description,
data,
slug: id,
}));
const backUrl = SITE.showBackButton ? `?from=${Astro.url.pathname}` : "/";
---
<Layout title={`Search | ${SITE.title}`}>
<Header />
<Main pageTitle="Search" pageDesc="Search any article ...">
<SearchBar client:load searchList={searchList} backUrl={backUrl} />
<!-- Search bar -->
</Main>
<Footer />
</Layout>
+1 -1
View File
@@ -5,7 +5,7 @@ import Main from "@/layouts/Main.astro";
import Layout from "@/layouts/Layout.astro";
import Header from "@/components/Header.astro";
import Footer from "@/components/Footer.astro";
import Card from "@/components/Card";
import Card from "@/components/Card.astro";
import Pagination from "@/components/Pagination.astro";
import getUniqueTags from "@/utils/getUniqueTags";
import getPostsByTag from "@/utils/getPostsByTag";
-9
View File
@@ -1,12 +1,3 @@
import type { FontStyle, FontWeight } from "satori";
export type FontOptions = {
name: string;
data: ArrayBuffer;
weight: FontWeight | undefined;
style: FontStyle | undefined;
};
async function loadGoogleFont(
font: string,
text: string
+229
View File
@@ -0,0 +1,229 @@
import satori from "satori";
// import { html } from "satori-html";
import { SITE } from "@/config";
import loadGoogleFonts from "../loadGoogleFont";
// const markup = html`<div
// style={{
// background: "#fefbfb",
// width: "100%",
// height: "100%",
// display: "flex",
// alignItems: "center",
// justifyContent: "center",
// }}
// >
// <div
// style={{
// position: "absolute",
// top: "-1px",
// right: "-1px",
// border: "4px solid #000",
// background: "#ecebeb",
// opacity: "0.9",
// borderRadius: "4px",
// display: "flex",
// justifyContent: "center",
// margin: "2.5rem",
// width: "88%",
// height: "80%",
// }}
// />
// <div
// style={{
// border: "4px solid #000",
// background: "#fefbfb",
// borderRadius: "4px",
// display: "flex",
// justifyContent: "center",
// margin: "2rem",
// width: "88%",
// height: "80%",
// }}
// >
// <div
// style={{
// display: "flex",
// flexDirection: "column",
// justifyContent: "space-between",
// margin: "20px",
// width: "90%",
// height: "90%",
// }}
// >
// <p
// style={{
// fontSize: 72,
// fontWeight: "bold",
// maxHeight: "84%",
// overflow: "hidden",
// }}
// >
// {post.data.title}
// </p>
// <div
// style={{
// display: "flex",
// justifyContent: "space-between",
// width: "100%",
// marginBottom: "8px",
// fontSize: 28,
// }}
// >
// <span>
// by{" "}
// <span
// style={{
// color: "transparent",
// }}
// >
// "
// </span>
// <span style={{ overflow: "hidden", fontWeight: "bold" }}>
// {post.data.author}
// </span>
// </span>
// <span style={{ overflow: "hidden", fontWeight: "bold" }}>
// {SITE.title}
// </span>
// </div>
// </div>
// </div>
// </div>`;
export default async post => {
return satori(
{
type: "div",
props: {
style: {
background: "#fefbfb",
width: "100%",
height: "100%",
display: "flex",
alignItems: "center",
justifyContent: "center",
},
children: [
{
type: "div",
props: {
style: {
position: "absolute",
top: "-1px",
right: "-1px",
border: "4px solid #000",
background: "#ecebeb",
opacity: "0.9",
borderRadius: "4px",
display: "flex",
justifyContent: "center",
margin: "2.5rem",
width: "88%",
height: "80%",
},
},
},
{
type: "div",
props: {
style: {
border: "4px solid #000",
background: "#fefbfb",
borderRadius: "4px",
display: "flex",
justifyContent: "center",
margin: "2rem",
width: "88%",
height: "80%",
},
children: {
type: "div",
props: {
style: {
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
margin: "20px",
width: "90%",
height: "90%",
},
children: [
{
type: "p",
props: {
style: {
fontSize: 72,
fontWeight: "bold",
maxHeight: "84%",
overflow: "hidden",
},
children: post.data.title,
},
},
{
type: "div",
props: {
style: {
display: "flex",
justifyContent: "space-between",
width: "100%",
marginBottom: "8px",
fontSize: 28,
},
children: [
{
type: "span",
props: {
children: [
"by ",
{
type: "span",
props: {
style: { color: "transparent" },
children: '"',
},
},
{
type: "span",
props: {
style: {
overflow: "hidden",
fontWeight: "bold",
},
children: post.data.author,
},
},
],
},
},
{
type: "span",
props: {
style: { overflow: "hidden", fontWeight: "bold" },
children: SITE.title,
},
},
],
},
},
],
},
},
},
},
],
},
},
{
width: 1200,
height: 630,
embedFont: true,
fonts: await loadGoogleFonts(
post.data.title + post.data.author + SITE.title + "by"
),
}
);
};
-106
View File
@@ -1,106 +0,0 @@
import satori from "satori";
import type { CollectionEntry } from "astro:content";
import { SITE } from "@/config";
import loadGoogleFonts, { type FontOptions } from "../loadGoogleFont";
export default async (post: CollectionEntry<"blog">) => {
return satori(
<div
style={{
background: "#fefbfb",
width: "100%",
height: "100%",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<div
style={{
position: "absolute",
top: "-1px",
right: "-1px",
border: "4px solid #000",
background: "#ecebeb",
opacity: "0.9",
borderRadius: "4px",
display: "flex",
justifyContent: "center",
margin: "2.5rem",
width: "88%",
height: "80%",
}}
/>
<div
style={{
border: "4px solid #000",
background: "#fefbfb",
borderRadius: "4px",
display: "flex",
justifyContent: "center",
margin: "2rem",
width: "88%",
height: "80%",
}}
>
<div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
margin: "20px",
width: "90%",
height: "90%",
}}
>
<p
style={{
fontSize: 72,
fontWeight: "bold",
maxHeight: "84%",
overflow: "hidden",
}}
>
{post.data.title}
</p>
<div
style={{
display: "flex",
justifyContent: "space-between",
width: "100%",
marginBottom: "8px",
fontSize: 28,
}}
>
<span>
by{" "}
<span
style={{
color: "transparent",
}}
>
"
</span>
<span style={{ overflow: "hidden", fontWeight: "bold" }}>
{post.data.author}
</span>
</span>
<span style={{ overflow: "hidden", fontWeight: "bold" }}>
{SITE.title}
</span>
</div>
</div>
</div>
</div>,
{
width: 1200,
height: 630,
embedFont: true,
fonts: (await loadGoogleFonts(
post.data.title + post.data.author + SITE.title + "by"
)) as FontOptions[],
}
);
};
+128
View File
@@ -0,0 +1,128 @@
import satori from "satori";
import { SITE } from "@/config";
import loadGoogleFonts from "../loadGoogleFont";
export default async () => {
return satori(
{
type: "div",
props: {
style: {
background: "#fefbfb",
width: "100%",
height: "100%",
display: "flex",
alignItems: "center",
justifyContent: "center",
},
children: [
{
type: "div",
props: {
style: {
position: "absolute",
top: "-1px",
right: "-1px",
border: "4px solid #000",
background: "#ecebeb",
opacity: "0.9",
borderRadius: "4px",
display: "flex",
justifyContent: "center",
margin: "2.5rem",
width: "88%",
height: "80%",
},
},
},
{
type: "div",
props: {
style: {
border: "4px solid #000",
background: "#fefbfb",
borderRadius: "4px",
display: "flex",
justifyContent: "center",
margin: "2rem",
width: "88%",
height: "80%",
},
children: {
type: "div",
props: {
style: {
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
margin: "20px",
width: "90%",
height: "90%",
},
children: [
{
type: "div",
props: {
style: {
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
height: "90%",
maxHeight: "90%",
overflow: "hidden",
textAlign: "center",
},
children: [
{
type: "p",
props: {
style: { fontSize: 72, fontWeight: "bold" },
children: SITE.title,
},
},
{
type: "p",
props: {
style: { fontSize: 28 },
children: SITE.desc,
},
},
],
},
},
{
type: "div",
props: {
style: {
display: "flex",
justifyContent: "flex-end",
width: "100%",
marginBottom: "8px",
fontSize: 28,
},
children: {
type: "span",
props: {
style: { overflow: "hidden", fontWeight: "bold" },
children: new URL(SITE.website).hostname,
},
},
},
},
],
},
},
},
},
],
},
},
{
width: 1200,
height: 630,
embedFont: true,
fonts: await loadGoogleFonts(SITE.title + SITE.desc + SITE.website),
}
);
};
-97
View File
@@ -1,97 +0,0 @@
import satori from "satori";
import { SITE } from "@/config";
import loadGoogleFonts, { type FontOptions } from "../loadGoogleFont";
export default async () => {
return satori(
<div
style={{
background: "#fefbfb",
width: "100%",
height: "100%",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<div
style={{
position: "absolute",
top: "-1px",
right: "-1px",
border: "4px solid #000",
background: "#ecebeb",
opacity: "0.9",
borderRadius: "4px",
display: "flex",
justifyContent: "center",
margin: "2.5rem",
width: "88%",
height: "80%",
}}
/>
<div
style={{
border: "4px solid #000",
background: "#fefbfb",
borderRadius: "4px",
display: "flex",
justifyContent: "center",
margin: "2rem",
width: "88%",
height: "80%",
}}
>
<div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
margin: "20px",
width: "90%",
height: "90%",
}}
>
<div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
height: "90%",
maxHeight: "90%",
overflow: "hidden",
textAlign: "center",
}}
>
<p style={{ fontSize: 72, fontWeight: "bold" }}>{SITE.title}</p>
<p style={{ fontSize: 28 }}>{SITE.desc}</p>
</div>
<div
style={{
display: "flex",
justifyContent: "flex-end",
width: "100%",
marginBottom: "8px",
fontSize: 28,
}}
>
<span style={{ overflow: "hidden", fontWeight: "bold" }}>
{new URL(SITE.website).hostname}
</span>
</div>
</div>
</div>
</div>,
{
width: 1200,
height: 630,
embedFont: true,
fonts: (await loadGoogleFonts(
SITE.title + SITE.desc + SITE.website
)) as FontOptions[],
}
);
};