Redone the entire blog site in astro
6
.eslintignore
Normal file
|
@ -0,0 +1,6 @@
|
|||
.husky
|
||||
.vscode
|
||||
node_modules
|
||||
public
|
||||
dist
|
||||
.yarn
|
23
.eslintrc.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
node: true,
|
||||
es2022: true,
|
||||
browser: true,
|
||||
},
|
||||
extends: ["eslint:recommended", "plugin:astro/recommended"],
|
||||
parserOptions: {
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module",
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ["*.astro"],
|
||||
parser: "astro-eslint-parser",
|
||||
parserOptions: {
|
||||
parser: "@typescript-eslint/parser",
|
||||
extraFileExtensions: [".astro"],
|
||||
},
|
||||
rules: {},
|
||||
},
|
||||
],
|
||||
};
|
45
.gitignore
vendored
|
@ -1,20 +1,35 @@
|
|||
# Dependencies
|
||||
/node_modules
|
||||
# build output
|
||||
dist/
|
||||
.output/
|
||||
|
||||
# Production
|
||||
/build
|
||||
|
||||
# Generated files
|
||||
.docusaurus
|
||||
.cache-loader
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# ignore .astro directory
|
||||
.astro
|
||||
|
||||
# ignore Jampack cache files
|
||||
.jampack/
|
||||
|
||||
# yarn
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
.pnp.*
|
||||
|
|
4
.husky/pre-commit
Normal file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx lint-staged
|
4
.markdownlint.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"MD033": false,
|
||||
"MD013": false
|
||||
}
|
2
.npmrc
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Expose Astro dependencies for `pnpm` users
|
||||
shamefully-hoist=true
|
13
.prettierignore
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Ignore everything
|
||||
/*
|
||||
|
||||
# Except these files & folders
|
||||
!/src
|
||||
!/public
|
||||
!/.github
|
||||
!tsconfig.json
|
||||
!astro.config.ts
|
||||
!package.json
|
||||
!.prettierrc
|
||||
!.eslintrc.js
|
||||
!README.md
|
20
.prettierrc
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"arrowParens": "avoid",
|
||||
"semi": true,
|
||||
"tabWidth": 2,
|
||||
"printWidth": 80,
|
||||
"singleQuote": false,
|
||||
"jsxSingleQuote": false,
|
||||
"trailingComma": "es5",
|
||||
"bracketSpacing": true,
|
||||
"endOfLine": "lf",
|
||||
"plugins": ["prettier-plugin-astro", "prettier-plugin-tailwindcss"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.astro",
|
||||
"options": {
|
||||
"parser": "astro"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
4
.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"recommendations": ["astro-build.astro-vscode"],
|
||||
"unwantedRecommendations": []
|
||||
}
|
11
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "./node_modules/.bin/astro dev",
|
||||
"name": "Development server",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
159
AstroPaper-lighthouse-score.svg
Normal file
|
@ -0,0 +1,159 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="theme--agnostic" fill="none" width="1000" height="330">
|
||||
<style>
|
||||
.gauge-base {
|
||||
opacity: 0.1
|
||||
}
|
||||
|
||||
.gauge-arc {
|
||||
fill: none;
|
||||
animation-delay: 250ms;
|
||||
stroke-linecap: round;
|
||||
transform: rotate(-90deg);
|
||||
transform-origin: 100px 60px;
|
||||
animation: load-gauge 1s ease forwards
|
||||
}
|
||||
|
||||
.guage-text {
|
||||
font-size: 40px;
|
||||
font-family: monospace;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.guage-red {
|
||||
color: #ff4e42;
|
||||
fill: #ff4e42;
|
||||
stroke: #ff4e42
|
||||
}
|
||||
.guage-orange {
|
||||
color: #ffa400;
|
||||
fill: #ffa400;
|
||||
stroke: #ffa400
|
||||
}
|
||||
.guage-green {
|
||||
color: #0cce6b;
|
||||
fill: #0cce6b;
|
||||
stroke: #0cce6b
|
||||
}
|
||||
.theme--agnostic .guage-undefined {
|
||||
color: #5c5c5c;
|
||||
fill: #5c5c5c;
|
||||
stroke: #5c5c5c
|
||||
}
|
||||
.theme--light .guage-undefined {
|
||||
color: #1e1e1e;
|
||||
fill: #1e1e1e;
|
||||
stroke: #1e1e1e
|
||||
}
|
||||
.theme--dark .guage-undefined {
|
||||
color: #f5f5f5;
|
||||
fill: #f5f5f5;
|
||||
stroke: #f5f5f5
|
||||
}
|
||||
|
||||
.guage-title {
|
||||
stroke: none;
|
||||
font-size: 26px;
|
||||
line-height: 26px;
|
||||
font-family: Roboto, Halvetica, Arial, sans-serif
|
||||
}
|
||||
.metric.guage-title {
|
||||
font-family: 'Courier New', Courier, monospace
|
||||
}
|
||||
.theme--agnostic .guage-title {
|
||||
color: #737373;
|
||||
fill: #737373
|
||||
}
|
||||
.theme--light .guage-title {
|
||||
color: #212121;
|
||||
fill: #212121
|
||||
}
|
||||
.theme--dark .guage-title {
|
||||
color: #f5f5f5;
|
||||
fill: #f5f5f5
|
||||
}
|
||||
|
||||
@keyframes load-gauge {
|
||||
from {
|
||||
stroke-dasharray: 0 352.858
|
||||
}
|
||||
}
|
||||
.lh-gauge--pwa__disc {
|
||||
fill: #e0e0e0
|
||||
}
|
||||
.lh-gauge--pwa__logo {
|
||||
position: relative;
|
||||
fill: #b0b0b0
|
||||
}
|
||||
.lh-gauge--pwa__invisible {
|
||||
display: none
|
||||
}
|
||||
.lh-gauge--pwa__visible {
|
||||
display: inline
|
||||
}
|
||||
.guage-invisible {
|
||||
display: none
|
||||
}
|
||||
.lh-gauge--pwa__logo--primary-color {
|
||||
fill: #304ffe
|
||||
}
|
||||
.theme--agnostic .lh-gauge--pwa__logo--secondary-color {
|
||||
fill: #787878
|
||||
}
|
||||
.theme--light .lh-gauge--pwa__logo--secondary-color {
|
||||
fill: #3d3d3d
|
||||
}
|
||||
.theme--dark .lh-gauge--pwa__logo--secondary-color {
|
||||
fill: #d8b6b6
|
||||
}
|
||||
.theme--light #svg_2 {
|
||||
stroke: #00000022
|
||||
}
|
||||
.theme--agnostic #svg_2 {
|
||||
stroke: #616161
|
||||
}
|
||||
.theme--light #svg_2 {
|
||||
stroke: #00000022
|
||||
}
|
||||
.theme--dark #svg_2 {
|
||||
stroke: #f5f5f566
|
||||
}
|
||||
</style>
|
||||
<svg class="guage-div guage-perf guage-green" viewBox="0 0 200 200" width="200" height="200" x="100" y="0">
|
||||
<circle class="gauge-base" r="56" cx="100" cy="60" stroke-width="8"/>
|
||||
<circle class="gauge-arc guage-arc-1" r="56" cx="100" cy="60" stroke-width="8" style="stroke-dasharray: 351.858, 351.858;"/>
|
||||
<text class="guage-text" x="100px" y="60px" alignment-baseline="central" dominant-baseline="central" text-anchor="middle">100</text>
|
||||
<text class="guage-title" x="100px" y="160px" alignment-baseline="central" dominant-baseline="central" text-anchor="middle">Performance</text>
|
||||
</svg>,<svg class="guage-div guage-perf guage-green" viewBox="0 0 200 200" width="200" height="200" x="300" y="0">
|
||||
<circle class="gauge-base" r="56" cx="100" cy="60" stroke-width="8"/>
|
||||
<circle class="gauge-arc guage-arc-1" r="56" cx="100" cy="60" stroke-width="8" style="stroke-dasharray: 351.858, 351.858;"/>
|
||||
<text class="guage-text" x="100px" y="60px" alignment-baseline="central" dominant-baseline="central" text-anchor="middle">100</text>
|
||||
<text class="guage-title" x="100px" y="160px" alignment-baseline="central" dominant-baseline="central" text-anchor="middle">Accessibility</text>
|
||||
</svg>,<svg class="guage-div guage-perf guage-green" viewBox="0 0 200 200" width="200" height="200" x="500" y="0">
|
||||
<circle class="gauge-base" r="56" cx="100" cy="60" stroke-width="8"/>
|
||||
<circle class="gauge-arc guage-arc-1" r="56" cx="100" cy="60" stroke-width="8" style="stroke-dasharray: 351.858, 351.858;"/>
|
||||
<text class="guage-text" x="100px" y="60px" alignment-baseline="central" dominant-baseline="central" text-anchor="middle">100</text>
|
||||
<text class="guage-title" x="100px" y="160px" alignment-baseline="central" dominant-baseline="central" text-anchor="middle">Best Practices</text>
|
||||
</svg>,<svg class="guage-div guage-perf guage-green" viewBox="0 0 200 200" width="200" height="200" x="700" y="0">
|
||||
<circle class="gauge-base" r="56" cx="100" cy="60" stroke-width="8"/>
|
||||
<circle class="gauge-arc guage-arc-1" r="56" cx="100" cy="60" stroke-width="8" style="stroke-dasharray: 351.858, 351.858;"/>
|
||||
<text class="guage-text" x="100px" y="60px" alignment-baseline="central" dominant-baseline="central" text-anchor="middle">100</text>
|
||||
<text class="guage-title" x="100px" y="160px" alignment-baseline="central" dominant-baseline="central" text-anchor="middle">SEO</text>
|
||||
</svg>
|
||||
<svg width="604" height="76" x="200" y="250">
|
||||
<g>
|
||||
<rect fill="none" id="canvas_background" height="80" width="604" y="-1" x="-1"/>
|
||||
<g display="none" overflow="visible" y="0" x="0" height="100%" width="100%" id="canvasGrid">
|
||||
<rect fill="url(#gridpattern)" stroke-width="0" y="0" x="0" height="100%" width="100%"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<rect fill-opacity="0" stroke-width="2" rx="40" id="svg_2" height="72" width="600" y="1" x="0" fill="#000000"/>
|
||||
<rect stroke="#000" rx="8" id="svg_3" height="14" width="48" y="30" x="35" stroke-opacity="null" stroke-width="0" fill="#ff4e42"/>
|
||||
<rect stroke="#000" rx="6" id="svg_4" height="14" width="48" y="30" x="220" stroke-opacity="null" stroke-width="0" fill="#ffa400"/>
|
||||
<rect stroke="#000" rx="6" id="svg_5" height="14" width="48" y="30" x="410" stroke-opacity="null" stroke-width="0" fill="#0cce6b"/>
|
||||
<text class="metric guage-title" xml:space="preserve" text-anchor="start" font-size="26" id="svg_6" y="45" x="100" stroke-opacity="null" stroke-width="0" stroke="#000">0-49</text>
|
||||
<text class="metric guage-title" xml:space="preserve" text-anchor="start" font-size="26" id="svg_7" y="45" x="280" stroke-opacity="null" stroke-width="0" stroke="#000">50-89</text>
|
||||
<text class="metric guage-title" xml:space="preserve" text-anchor="start" font-size="26" id="svg_8" y="45" x="470" stroke-opacity="null" stroke-width="0" stroke="#000">90-100</text>
|
||||
</g>
|
||||
</svg>
|
||||
</svg>
|
After Width: | Height: | Size: 6.1 KiB |
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 Sat Naing
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
40
astro.config.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { defineConfig } from "astro/config";
|
||||
import tailwind from "@astrojs/tailwind";
|
||||
import react from "@astrojs/react";
|
||||
import remarkToc from "remark-toc";
|
||||
import remarkCollapse from "remark-collapse";
|
||||
import sitemap from "@astrojs/sitemap";
|
||||
import { SITE } from "./src/config";
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: SITE.website,
|
||||
integrations: [
|
||||
tailwind({
|
||||
applyBaseStyles: false,
|
||||
}),
|
||||
react(),
|
||||
sitemap(),
|
||||
],
|
||||
markdown: {
|
||||
remarkPlugins: [
|
||||
remarkToc,
|
||||
[
|
||||
remarkCollapse,
|
||||
{
|
||||
test: "Table of contents",
|
||||
},
|
||||
],
|
||||
],
|
||||
shikiConfig: {
|
||||
theme: "one-dark-pro",
|
||||
wrap: true,
|
||||
},
|
||||
},
|
||||
vite: {
|
||||
optimizeDeps: {
|
||||
exclude: ["@resvg/resvg-js"],
|
||||
},
|
||||
},
|
||||
scopedStyleStrategy: "where",
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
module.exports = {
|
||||
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
|
||||
};
|
|
@ -1,5 +0,0 @@
|
|||
toastie_t0ast:
|
||||
name: toastie_t0ast
|
||||
title: Head gremlin
|
||||
url: https://www.toastiet0ast.com
|
||||
image_url: https://toastielab.dev/toastie_t0ast.png
|
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
slug: welcome
|
||||
title: Welcome to my blog
|
||||
authors: [toastie_t0ast]
|
||||
tags: [welcome]
|
||||
---
|
||||
|
||||
Welcome to my blog here you can find what I get up to
|
||||
|
||||
Thanks for reading!
|
||||
|
||||
EmotionChild
|
|
@ -1,114 +0,0 @@
|
|||
// @ts-check
|
||||
// Note: type annotations allow type checking and IDEs autocompletion
|
||||
|
||||
const lightCodeTheme = require('prism-react-renderer/themes/github');
|
||||
const darkCodeTheme = require('prism-react-renderer/themes/dracula');
|
||||
|
||||
/** @type {import('@docusaurus/types').Config} */
|
||||
const config = {
|
||||
title: 'Toasties Blog',
|
||||
tagline: 'The chaotic world of Toastie_t0ast',
|
||||
favicon: 'img/favicon.ico',
|
||||
|
||||
// Set the production url of your site here
|
||||
url: 'https://blog.toastiet0ast.com',
|
||||
// Set the /<baseUrl>/ pathname under which your site is served
|
||||
// For GitHub pages deployment, it is often '/<projectName>/'
|
||||
baseUrl: '/',
|
||||
|
||||
// GitHub pages deployment config.
|
||||
// If you aren't using GitHub pages, you don't need these.
|
||||
organizationName: 'Emotions-stuff', // Usually your GitHub org/user name.
|
||||
projectName: 'Emotion-Blog', // Usually your repo name.
|
||||
|
||||
onBrokenLinks: 'throw',
|
||||
onBrokenMarkdownLinks: 'warn',
|
||||
|
||||
// Even if you don't use internalization, you can use this field to set useful
|
||||
// metadata like html lang. For example, if your site is Chinese, you may want
|
||||
// to replace "en" with "zh-Hans".
|
||||
i18n: {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en'],
|
||||
},
|
||||
|
||||
presets: [
|
||||
[
|
||||
'classic',
|
||||
/** @type {import('@docusaurus/preset-classic').Options} */
|
||||
({
|
||||
docs: false,
|
||||
blog: {
|
||||
routeBasePath: '/',
|
||||
showReadingTime: true,
|
||||
readingTime: ({content, frontMatter, defaultReadingTime}) =>
|
||||
frontMatter.hide_reading_time ? undefined : defaultReadingTime({content}),
|
||||
blogTitle: 'Toasties Blog',
|
||||
blogDescription: 'Here you can find blog posts about what Toastie has gotten up to',
|
||||
postsPerPage: 'ALL',
|
||||
feedOptions: {
|
||||
type: 'all',
|
||||
copyright: `Copyright © ${new Date().getFullYear()} toastie_t0ast.`,
|
||||
},
|
||||
},
|
||||
theme: {
|
||||
customCss: require.resolve('./src/css/custom.css'),
|
||||
},
|
||||
}),
|
||||
],
|
||||
],
|
||||
|
||||
themeConfig:
|
||||
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
||||
({
|
||||
// Replace with your project's social card
|
||||
navbar: {
|
||||
title: 'Toasties Blog',
|
||||
logo: {
|
||||
alt: 'My Site Logo',
|
||||
src: 'img/favicon.png',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
href: 'https://github.com/Emotions-stuff/Emotion-Blog',
|
||||
label: 'GitHub',
|
||||
position: 'right',
|
||||
},
|
||||
],
|
||||
},
|
||||
footer: {
|
||||
style: 'dark',
|
||||
links: [
|
||||
{
|
||||
title: 'Community',
|
||||
items: [
|
||||
{
|
||||
label: 'Discord',
|
||||
href: 'https://discord.com/invite/aAsGMFPfdu',
|
||||
},
|
||||
{
|
||||
label: 'Twitter',
|
||||
href: 'https://twitter.com/Computergeex5',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'More',
|
||||
items: [
|
||||
{
|
||||
label: 'GitHub',
|
||||
href: 'https://github.com/Emotions-stuff/Emotion-Blog',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
copyright: `Copyright © ${new Date().getFullYear()} ToastieChild`,
|
||||
},
|
||||
prism: {
|
||||
theme: lightCodeTheme,
|
||||
darkTheme: darkCodeTheme,
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
module.exports = config;
|
88
package.json
|
@ -1,44 +1,62 @@
|
|||
{
|
||||
"name": "emotion-blog",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"name": "toasite-blog",
|
||||
"version": "3.0.0",
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
"start": "docusaurus start",
|
||||
"build": "docusaurus build",
|
||||
"swizzle": "docusaurus swizzle",
|
||||
"deploy": "docusaurus deploy",
|
||||
"clear": "docusaurus clear",
|
||||
"serve": "docusaurus serve",
|
||||
"write-translations": "docusaurus write-translations",
|
||||
"write-heading-ids": "docusaurus write-heading-ids"
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build && jampack ./dist",
|
||||
"preview": "astro preview",
|
||||
"sync": "astro sync",
|
||||
"astro": "astro",
|
||||
"format:check": "prettier --plugin-search-dir=. --check .",
|
||||
"format": "prettier --plugin-search-dir=. --write .",
|
||||
"cz": "cz",
|
||||
"prepare": "husky install",
|
||||
"lint": "eslint ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "2.4.1",
|
||||
"@docusaurus/preset-classic": "2.4.1",
|
||||
"@docusaurus/types": "^2.4.1",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"clsx": "^1.2.1",
|
||||
"prism-react-renderer": "^1.3.5",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
"@astrojs/check": "^0.2.0",
|
||||
"@astrojs/rss": "^3.0.0",
|
||||
"@resvg/resvg-js": "^2.4.1",
|
||||
"astro": "^3.1.3",
|
||||
"fuse.js": "^6.6.2",
|
||||
"github-slugger": "^2.0.0",
|
||||
"remark-collapse": "^0.1.2",
|
||||
"remark-toc": "^9.0.0",
|
||||
"satori": "^0.10.8",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "2.4.1"
|
||||
"@astrojs/react": "^3.0.2",
|
||||
"@astrojs/sitemap": "^3.0.0",
|
||||
"@astrojs/tailwind": "^5.0.0",
|
||||
"@divriots/jampack": "^0.20.2",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@types/github-slugger": "^1.3.0",
|
||||
"@types/react": "^18.2.22",
|
||||
"@typescript-eslint/parser": "^6.7.3",
|
||||
"astro-eslint-parser": "^0.15.0",
|
||||
"commitizen": "^4.3.0",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"eslint": "^8.50.0",
|
||||
"eslint-plugin-astro": "^0.29.0",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^14.0.1",
|
||||
"prettier": "^3.0.3",
|
||||
"prettier-plugin-astro": "^0.12.0",
|
||||
"prettier-plugin-tailwindcss": "^0.5.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.5%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx,md,mdx,json}": [
|
||||
"prettier --plugin-search-dir=. --write"
|
||||
]
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.14"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
6647
pnpm-lock.yaml
Normal file
361
public/assets/dev.svg
Normal file
|
@ -0,0 +1,361 @@
|
|||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="865.76" height="682.89" viewBox="0 0 865.76 682.89">
|
||||
|
||||
<defs>
|
||||
|
||||
<style xmlns="http://www.w3.org/1999/xhtml">*, body, html { -webkit-font-smoothing: antialiased; }
|
||||
img, svg { max-width: 100%; }
|
||||
</style>
|
||||
|
||||
</defs>
|
||||
|
||||
<path d="M391.82,532.2c-15.44,2.82-87.85,18.09-73.28,55a33.24,33.24,0,0,0,9.74,13.13c18.18,15.25,83.33,52.58,272.06,32.22,10.69-1.15,21.42-1.86,32.17-2.06,49.73-.92,206-9.34,202-78.54,0,0-2.07-38.74-95.7-26.87l-71.21-4.43s-160.55-12.38-268.7,10.11C396.57,531.29,394.2,531.77,391.82,532.2Z" fill="#787878" data-primary="true"/>
|
||||
|
||||
<path d="M391.82,532.2c-15.44,2.82-87.85,18.09-73.28,55a33.24,33.24,0,0,0,9.74,13.13c18.18,15.25,83.33,52.58,272.06,32.22,10.69-1.15,21.42-1.86,32.17-2.06,49.73-.92,206-9.34,202-78.54,0,0-2.07-38.74-95.7-26.87l-71.21-4.43s-160.55-12.38-268.7,10.11C396.57,531.29,394.2,531.77,391.82,532.2Z" fill="#fff" opacity="0.7"/>
|
||||
|
||||
<path d="M503.08,522.3C179.26,552.79,133.91,359.63,133.91,359.63c-24.79-67.13-3.45-111,27.66-152.68a303.36,303.36,0,0,1,117.77-94.5c74.9-34.06,126.36-41,126.36-41S622.58,15.68,735.64,183.7c0,0,108,135.55,37.54,221.14,0,0-35.14,47.34-127.63,82.89l-69.3,20.46A387.7,387.7,0,0,1,503.08,522.3Z" fill="#787878" data-primary="true"/>
|
||||
|
||||
<path d="M503.08,522.3C179.26,552.79,133.91,359.63,133.91,359.63c-24.79-67.13-3.45-111,27.66-152.68a303.36,303.36,0,0,1,117.77-94.5c74.9-34.06,126.36-41,126.36-41S622.58,15.68,735.64,183.7c0,0,108,135.55,37.54,221.14,0,0-35.14,47.34-127.63,82.89l-69.3,20.46A387.7,387.7,0,0,1,503.08,522.3Z" fill="#fff" opacity="0.7"/>
|
||||
|
||||
<rect x="104.67" y="206.46" width="463.2" height="348.88" fill="#fff"/>
|
||||
|
||||
<rect x="108.43" y="206.46" width="459.44" height="35.42" fill="#e6e6e6"/>
|
||||
|
||||
<rect x="128.82" y="259.06" width="104.13" height="104.13" fill="#787878" opacity="0.29" data-primary="true"/>
|
||||
|
||||
<rect x="713.86" y="369.62" width="5.37" height="37.57" fill="#999"/>
|
||||
|
||||
<polygon points="664.89 442.18 664.89 554.44 672.53 554.44 676.93 436.58 664.89 442.18" fill="#ccc"/>
|
||||
|
||||
<polygon points="711.71 420.08 711.71 537.08 719.36 537.08 723.52 414.71 711.71 420.08" fill="#ccc"/>
|
||||
|
||||
<polygon points="668.23 434.1 733.18 405.05 703.86 399.96 670.01 385.44 668.23 434.1" fill="#ccc"/>
|
||||
|
||||
<path d="M656.14,446.25l77-35.83v-5.37L668.23,434.1S660.68,442.36,656.14,446.25Z" fill="#b3b3b3"/>
|
||||
|
||||
<path d="M693.46,271.94H734a4.55,4.55,0,0,1,4.55,4.55v67.37a0,0,0,0,1,0,0H693.46a0,0,0,0,1,0,0V271.94A0,0,0,0,1,693.46,271.94Z" fill="#999"/>
|
||||
|
||||
<rect x="241.54" y="44.36" width="325.8" height="139.55" fill="#787878" data-primary="true"/>
|
||||
|
||||
<rect x="263.01" y="83.01" width="100.91" height="65.48" fill="#fff" opacity="0.3"/>
|
||||
|
||||
<g opacity="0.3">
|
||||
|
||||
<path d="M297.36,131.59a1.07,1.07,0,0,1-.76-.32l-14.79-14.76a1.08,1.08,0,0,1,0-1.5l14.79-15.56a1.07,1.07,0,0,1,1.56,1.47l-14.07,14.81,14.05,14a1.07,1.07,0,0,1,0,1.52A1.09,1.09,0,0,1,297.36,131.59Z" fill="#fff"/>
|
||||
|
||||
</g>
|
||||
|
||||
<g opacity="0.3">
|
||||
|
||||
<path d="M328.73,132.66a1.06,1.06,0,0,1-.76-.31,1.07,1.07,0,0,1,0-1.52l14-14L328,102a1.08,1.08,0,1,1,1.56-1.48l14.78,15.56a1.06,1.06,0,0,1,0,1.5l-14.78,14.77A1.07,1.07,0,0,1,328.73,132.66Z" fill="#fff"/>
|
||||
|
||||
</g>
|
||||
|
||||
<g opacity="0.3">
|
||||
|
||||
<path d="M305.56,131.59a1.08,1.08,0,0,1-1-1.56l14.34-28.18a1.08,1.08,0,1,1,1.92,1L306.51,131A1.07,1.07,0,0,1,305.56,131.59Z" fill="#fff"/>
|
||||
|
||||
</g>
|
||||
|
||||
<path d="M524.39,119.51H454.62a1.08,1.08,0,0,1,0-2.15h69.77a1.08,1.08,0,1,1,0,2.15Z" fill="#fff"/>
|
||||
|
||||
<path d="M540.5,132.39H454.62a1.08,1.08,0,0,1,0-2.15H540.5a1.08,1.08,0,0,1,0,2.15Z" fill="#fff"/>
|
||||
|
||||
<rect x="460.52" y="153.86" width="65.48" height="16.1" rx="7.5" fill="#fff" opacity="0.3"/>
|
||||
|
||||
<path d="M567.33,44.36V183.91H241.54s54.75-59.1,144.51-74c4.1-.68,8.24-1.12,12.38-1.4C426.41,106.6,557.79,95.18,567.33,44.36Z" fill="#fff" opacity="0.3"/>
|
||||
|
||||
<rect x="31.14" y="128.09" width="187.86" height="213.62" fill="#787878" data-primary="true"/>
|
||||
|
||||
<rect x="31.14" y="128.09" width="187.86" height="34.35" fill="#fff" opacity="0.3"/>
|
||||
|
||||
<rect x="46.17" y="173.18" width="57.97" height="57.97" fill="#282728" data-secondary="true"/>
|
||||
|
||||
<circle cx="164.78" cy="145.27" r="3.76" fill="#fff" opacity="0.3"/>
|
||||
|
||||
<circle cx="184.11" cy="145.27" r="3.76" fill="#fff" opacity="0.3"/>
|
||||
|
||||
<circle cx="203.43" cy="145.27" r="3.76" fill="#fff" opacity="0.3"/>
|
||||
|
||||
<path d="M170.69,192.5H117a1.07,1.07,0,1,1,0-2.14h53.67a1.07,1.07,0,0,1,0,2.14Z" fill="#fff"/>
|
||||
|
||||
<path d="M186.25,205.38h-68.7a1.07,1.07,0,0,1,0-2.14h68.7a1.07,1.07,0,1,1,0,2.14Z" fill="#fff"/>
|
||||
|
||||
<path d="M203.43,218.27H117.55a1.08,1.08,0,0,1,0-2.15h85.88a1.08,1.08,0,0,1,0,2.15Z" fill="#fff"/>
|
||||
|
||||
<path d="M168,287H84.28a1.08,1.08,0,1,1,0-2.15H168a1.08,1.08,0,0,1,0,2.15Z" fill="#fff"/>
|
||||
|
||||
<path d="M194.84,299.85H57.44a1.08,1.08,0,1,1,0-2.15h137.4a1.08,1.08,0,1,1,0,2.15Z" fill="#fff"/>
|
||||
|
||||
<path d="M168.54,312.73H83.74a1.08,1.08,0,1,1,0-2.15h84.8a1.08,1.08,0,1,1,0,2.15Z" fill="#fff"/>
|
||||
|
||||
<rect x="83.74" y="248.32" width="78.36" height="16.1" fill="#fff" opacity="0.3"/>
|
||||
|
||||
<rect x="256.57" y="259.06" width="66.55" height="17.18" fill="#787878" opacity="0.29" data-primary="true"/>
|
||||
|
||||
<path d="M308.78,293.79H256.57a1.08,1.08,0,1,1,0-2.15h52.21a1.08,1.08,0,1,1,0,2.15Z" fill="#e6e6e6"/>
|
||||
|
||||
<path d="M325.8,306.67H256.57a1.07,1.07,0,1,1,0-2.14H325.8a1.07,1.07,0,1,1,0,2.14Z" fill="#e6e6e6"/>
|
||||
|
||||
<path d="M339.76,319.55H256.57a1.07,1.07,0,1,1,0-2.14h83.19a1.07,1.07,0,0,1,0,2.14Z" fill="#e6e6e6"/>
|
||||
|
||||
<path d="M379.48,332.44H256.57a1.08,1.08,0,1,1,0-2.15H379.48a1.08,1.08,0,0,1,0,2.15Z" fill="#e6e6e6"/>
|
||||
|
||||
<rect x="256.57" y="348.15" width="154.58" height="15.03" fill="#787878" opacity="0.29" data-primary="true"/>
|
||||
|
||||
<path d="M252.45,400.29h-122a1.08,1.08,0,0,1,0-2.15h122a1.08,1.08,0,1,1,0,2.15Z" fill="#e6e6e6"/>
|
||||
|
||||
<path d="M353.18,400.29H268.91a1.08,1.08,0,0,1,0-2.15h84.27a1.08,1.08,0,0,1,0,2.15Z" fill="#e6e6e6"/>
|
||||
|
||||
<path d="M417.59,400.29H388.06a1.08,1.08,0,0,1,0-2.15h29.53a1.08,1.08,0,0,1,0,2.15Z" fill="#e6e6e6"/>
|
||||
|
||||
<rect x="256.57" y="396.53" width="5.37" height="5.37" fill="#787878" opacity="0.29" data-primary="true"/>
|
||||
|
||||
<rect x="360.69" y="396.53" width="5.37" height="5.37" fill="#787878" opacity="0.29" data-primary="true"/>
|
||||
|
||||
<rect x="373.57" y="396.53" width="5.37" height="5.37" fill="#ccc"/>
|
||||
|
||||
<path d="M223.29,429.16H131a1.08,1.08,0,0,1,0-2.15h92.32a1.08,1.08,0,0,1,0,2.15Z" fill="#e6e6e6"/>
|
||||
|
||||
<path d="M289.84,455.37H129.9a1.08,1.08,0,1,1,0-2.15H289.84a1.08,1.08,0,1,1,0,2.15Z" fill="#e6e6e6"/>
|
||||
|
||||
<path d="M325.27,429.16H255a1.08,1.08,0,1,1,0-2.15h70.31a1.08,1.08,0,0,1,0,2.15Z" fill="#e6e6e6"/>
|
||||
|
||||
<path d="M349.42,455.37h-36a1.08,1.08,0,0,1,0-2.15h36a1.08,1.08,0,0,1,0,2.15Z" fill="#e6e6e6"/>
|
||||
|
||||
<rect x="227.58" y="425.4" width="5.37" height="5.37" fill="#787878" opacity="0.29" data-primary="true"/>
|
||||
|
||||
<rect x="240.46" y="425.4" width="5.37" height="5.37" fill="#ccc"/>
|
||||
|
||||
<rect x="290.92" y="451.61" width="5.37" height="5.37" fill="#787878" opacity="0.29" data-primary="true"/>
|
||||
|
||||
<rect x="303.8" y="451.61" width="5.37" height="5.37" fill="#ccc"/>
|
||||
|
||||
<path d="M355.32,512.93H298.43a1.08,1.08,0,0,1,0-2.15h56.89a1.08,1.08,0,1,1,0,2.15Z" fill="#e6e6e6"/>
|
||||
|
||||
<path d="M416,512.93H388.06a1.08,1.08,0,0,1,0-2.15H416a1.08,1.08,0,1,1,0,2.15Z" fill="#e6e6e6"/>
|
||||
|
||||
<rect x="361.77" y="509.17" width="5.37" height="5.37" fill="#ccc"/>
|
||||
|
||||
<rect x="374.65" y="509.17" width="5.37" height="5.37" fill="#787878" opacity="0.29" data-primary="true"/>
|
||||
|
||||
<path d="M416,455.37H375.72a1.08,1.08,0,0,1,0-2.15H416a1.08,1.08,0,1,1,0,2.15Z" fill="#e6e6e6"/>
|
||||
|
||||
<rect x="353.18" y="451.61" width="5.37" height="5.37" fill="#ccc"/>
|
||||
|
||||
<rect x="366.06" y="451.61" width="5.37" height="5.37" fill="#787878" opacity="0.29" data-primary="true"/>
|
||||
|
||||
<path d="M205,485H131a1.08,1.08,0,0,1,0-2.15H205a1.08,1.08,0,0,1,0,2.15Z" fill="#e6e6e6"/>
|
||||
|
||||
<path d="M349.42,485h-52.6a1.08,1.08,0,0,1,0-2.15h52.6a1.08,1.08,0,0,1,0,2.15Z" fill="#e6e6e6"/>
|
||||
|
||||
<path d="M416,485H363.38a1.08,1.08,0,1,1,0-2.15H416a1.08,1.08,0,1,1,0,2.15Z" fill="#e6e6e6"/>
|
||||
|
||||
<rect x="207.19" y="481.26" width="5.37" height="5.37" fill="#ccc"/>
|
||||
|
||||
<rect x="220.07" y="481.26" width="5.37" height="5.37" fill="#787878" opacity="0.29" data-primary="true"/>
|
||||
|
||||
<rect x="231.88" y="481.26" width="5.37" height="5.37" fill="#ccc"/>
|
||||
|
||||
<path d="M256.57,512.93H131a1.08,1.08,0,0,1,0-2.15h125.6a1.08,1.08,0,0,1,0,2.15Z" fill="#e6e6e6"/>
|
||||
|
||||
<rect x="258.71" y="509.17" width="5.37" height="5.37" fill="#ccc"/>
|
||||
|
||||
<rect x="271.59" y="509.17" width="5.37" height="5.37" fill="#787878" opacity="0.29" data-primary="true"/>
|
||||
|
||||
<rect x="283.4" y="509.17" width="5.37" height="5.37" fill="#ccc"/>
|
||||
|
||||
<rect x="244.76" y="481.26" width="5.37" height="5.37" fill="#787878" opacity="0.29" data-primary="true"/>
|
||||
|
||||
<rect x="259.79" y="481.26" width="5.37" height="5.37" fill="#ccc"/>
|
||||
|
||||
<rect x="271.59" y="481.26" width="5.37" height="5.37" fill="#787878" opacity="0.29" data-primary="true"/>
|
||||
|
||||
<rect x="284.48" y="481.26" width="5.37" height="5.37" fill="#ccc"/>
|
||||
|
||||
<path d="M417.59,429.16H358a1.08,1.08,0,1,1,0-2.15h59.58a1.08,1.08,0,0,1,0,2.15Z" fill="#e6e6e6"/>
|
||||
|
||||
<rect x="330.63" y="425.4" width="5.37" height="5.37" fill="#787878" opacity="0.29" data-primary="true"/>
|
||||
|
||||
<rect x="343.52" y="425.4" width="5.37" height="5.37" fill="#ccc"/>
|
||||
|
||||
<rect x="51.53" y="436.18" width="103.05" height="64.41" fill="#787878" data-primary="true"/>
|
||||
|
||||
<g opacity="0.3">
|
||||
|
||||
<path d="M88.5,485.36a1.06,1.06,0,0,1-.74-.3l-15.5-14.83a1.06,1.06,0,0,1,0-1.54l15.49-15a1.07,1.07,0,0,1,1.52,0,1.08,1.08,0,0,1,0,1.52l-14.7,14.25,14.69,14.06a1.07,1.07,0,0,1,0,1.52A1.1,1.1,0,0,1,88.5,485.36Z" fill="#fff"/>
|
||||
|
||||
</g>
|
||||
|
||||
<g opacity="0.3">
|
||||
|
||||
<path d="M119.16,485.36a1.07,1.07,0,0,1-.74-1.84l14.69-14.26L118.42,455.2a1.07,1.07,0,0,1,1.48-1.55l15.5,14.83a1.07,1.07,0,0,1,.33.77,1.08,1.08,0,0,1-.32.78l-15.5,15A1.08,1.08,0,0,1,119.16,485.36Z" fill="#fff"/>
|
||||
|
||||
</g>
|
||||
|
||||
<g opacity="0.3">
|
||||
|
||||
<path d="M96.62,483.41a1.11,1.11,0,0,1-.5-.12,1.07,1.07,0,0,1-.45-1.45l14-26.83a1.08,1.08,0,1,1,1.91,1l-14,26.83A1.06,1.06,0,0,1,96.62,483.41Z" fill="#fff"/>
|
||||
|
||||
</g>
|
||||
|
||||
<rect x="434.76" y="367.48" width="11.81" height="208.25" fill="#999"/>
|
||||
|
||||
<rect x="441.2" y="367.48" width="5.37" height="208.25" opacity="0.1"/>
|
||||
|
||||
<rect x="471.26" y="368.01" width="11.81" height="172.29" fill="#999"/>
|
||||
|
||||
<rect x="477.7" y="368.01" width="5.37" height="172.29" opacity="0.1"/>
|
||||
|
||||
<rect x="728.89" y="367.48" width="11.81" height="208.25" fill="#999"/>
|
||||
|
||||
<rect x="735.33" y="367.48" width="5.37" height="208.25" opacity="0.1"/>
|
||||
|
||||
<rect x="758.95" y="354.06" width="11.81" height="186.25" fill="#999"/>
|
||||
|
||||
<rect x="765.39" y="354.06" width="5.37" height="186.25" opacity="0.1"/>
|
||||
|
||||
<path d="M688.1,271.94h40.53a4.55,4.55,0,0,1,4.55,4.55v67.37a0,0,0,0,1,0,0H688.1a0,0,0,0,1,0,0V271.94A0,0,0,0,1,688.1,271.94Z" fill="#b3b3b3"/>
|
||||
|
||||
<polygon points="421.88 364.26 477.27 336.37 786.88 336.37 750.36 364.26 421.88 364.26" fill="#ccc"/>
|
||||
|
||||
<path d="M542.11,559.63l-32.5,25.42S496,597.2,507.76,604.71c0,0,17.17,10.74,31.13-7.51l19.37-31.64Z" fill="#787878" data-primary="true"/>
|
||||
|
||||
<path d="M505.61,596.12c8,8.68,20.58,6.87,28.45-1,3.7-3.79,7-8.33,10.52-12.3,3.08-3.62,7.51-8.79,10.65-12.28-2.8,3.74-7.06,9.09-10,12.81-3.41,4.12-6.73,8.65-10.42,12.54-8.21,8.11-21.45,9.88-29.19.26Z" opacity="0.2"/>
|
||||
|
||||
<path d="M512.32,583.74c6.45-.09,13.31,2.42,17.35,7.63a15.61,15.61,0,0,1,2.79,5.84c-.26-.47-.51-1-.74-1.43a8.51,8.51,0,0,0-.81-1.37c-4-6.39-11.44-9.4-18.59-10.67Z" opacity="0.2"/>
|
||||
|
||||
<path d="M519.56,580c4.83-.65,11.72.93,12.9,6.4-2.62-4.61-8.1-5.41-12.9-6.4Z" opacity="0.2"/>
|
||||
|
||||
<path d="M523.86,575.73c4.82-.65,11.72.93,12.89,6.39-2.61-4.6-8.1-5.4-12.89-6.39Z" opacity="0.2"/>
|
||||
|
||||
<path d="M532.45,569.29c4.82-.65,11.72.93,12.89,6.39-2.61-4.61-8.1-5.4-12.89-6.39Z" opacity="0.2"/>
|
||||
|
||||
<path d="M550.16,544.06l-8,15.57s-3.32,4,1.25,6.48a8.52,8.52,0,0,0,4.06,1h7.9a3.61,3.61,0,0,0,2.94-1.51L568,551.93S554.41,546.7,550.16,544.06Z" fill="#f9b499"/>
|
||||
|
||||
<polygon points="548.32 510.23 551.84 520.86 557.18 505.66 548.32 510.23" fill="#f9b499"/>
|
||||
|
||||
<path d="M710.77,332.4c0,8.24-5.5,8.24-5.5,8.24l-15,5.37c-6.68,2.23-9.44,1.89-10.5,1.46a1.62,1.62,0,0,0-1.36.1.24.24,0,0,1-.08.06,11.71,11.71,0,0,1-3.82,1.75h0c-2.89.58-2.48-2.31-2.48-2.31a12.77,12.77,0,0,0,.2-1.54,9.91,9.91,0,0,0-5.8-9.37,26.59,26.59,0,0,0-4.77-1.68,6.38,6.38,0,0,0-3.07,0l-8.33,1.91H608.39L570,335.27a98.24,98.24,0,0,1,12.58-65.48,86.2,86.2,0,0,1,8.82-12.47c.72-.84,1.14-1.27,1.14-1.27l2.37-3.43s32,2.14,32.37,1.07,22.15-14,22.15-14l30.76,13a10.63,10.63,0,0,1,3.59,2.59c6.62,6.85,11.81,23.17,11.81,23.17l14,46.16A30.89,30.89,0,0,1,710.77,332.4Z" fill="#787878" data-primary="true"/>
|
||||
|
||||
<path d="M675.8,305s-30.74,5-53.75.22c-.59-.12-1.17-.27-1.75-.43A88.92,88.92,0,0,0,592.56,302l-22.06-.18-1.09,7.3h36.87s12,.39,21.7,3.61c0,0,9.66,3.22,29,1.07l21.82-2.66Z" fill="#282728" data-secondary="true"/>
|
||||
|
||||
<path d="M683.8,255.21c-20.39,2.6-56.89,14.58-56.89,14.58-8.59-6.44-35.49-12.47-35.49-12.47.72-.84,1.14-1.27,1.14-1.27l2.37-3.43s32,2.14,32.37,1.07,22.15-14,22.15-14l30.76,13A10.63,10.63,0,0,1,683.8,255.21Z" opacity="0.2"/>
|
||||
|
||||
<path d="M620.1,254.32a12.38,12.38,0,0,1-1.24.26c-7.26,1.28-14.75-1.87-20.74-8a43,43,0,0,1-10.73-19.86c-4.59-18.58,2.63-36.33,16.12-39.66s28.13,9,32.72,27.59S633.6,251,620.1,254.32Z" fill="#f9b499"/>
|
||||
|
||||
<ellipse cx="639.26" cy="215.05" rx="1.61" ry="3.22" fill="none" stroke="red" stroke-miterlimit="10" stroke-width="0.75"/>
|
||||
|
||||
<path d="M651.6,210.75s17.18,9.45-10.73,18.14a15.44,15.44,0,0,1-.54-3.65,15.8,15.8,0,0,1,.54-4.54l-8.59-4.36s-4.67.85-7.17-4.52c0,0-8.93,0-7.86-6.44,0,0-6.44,4.3-8.59-1.07,0,0-9.69,6.88-19.33-4.62,0,0-4.28,7.84-3,21,0,0-6.34,5.08-9.93.38a6.63,6.63,0,0,1-1.28-3.77,5.58,5.58,0,0,1,3.22-5.49s-7.77-2.89-7.56-9.28a10.2,10.2,0,0,1,1.41-4.67s1.61-4,7.63-2.31h0a19.17,19.17,0,0,1,3.1,1.24s-8.21-17.26,3.4-28.49c0,0,19.14-19.82,26.66,4.87,0,0,6.55-10.14,17-7.62h0a15.76,15.76,0,0,1,2.25.72s7.51,2,6.44,14.1c0,0,9.66-8.28,18.25,1.38,0,0,6.44,7.89,0,16.28C657,198.05,660.19,205.38,651.6,210.75Z" fill="#282728" data-secondary="true"/>
|
||||
|
||||
<path d="M590.41,197.6s-3.22-10.46,6.44-16.91c0,0,6.93-4.51,16.49,1.46a23.89,23.89,0,0,1,2.73,2.07,16.44,16.44,0,0,0,10.59,4.11s11-.47,12.6,12c0,0-12.35-10.72-21.47-5.54,0,0-4.83-15.22-17.72-10.93C600.07,183.91,592.56,186.06,590.41,197.6Z" opacity="0.2"/>
|
||||
|
||||
<path d="M579.79,195.56c-5.23.93-9,7-9,7a10.2,10.2,0,0,1,1.41-4.67S573.77,193.84,579.79,195.56Z" opacity="0.2"/>
|
||||
|
||||
<path d="M651.6,210.75s17.18,9.45-10.73,18.14a7.4,7.4,0,0,1-.54-3.65,7.26,7.26,0,0,1,.54-2.07l4.29-6.83h0c3.22-1.93,3.22-6.66,3.22-6.66a10.45,10.45,0,0,0,4.63-3.63,8.74,8.74,0,0,0,1.09-8.24c-2.23-5.68-8.94-4.09-8.94-4.09,2.15-16-7.52-10.85-7.52-10.85-1-14.52-7.22-17.15-7.61-17.3a15.76,15.76,0,0,1,2.25.72s7.51,2,6.44,14.1c0,0,9.66-8.28,18.25,1.38,0,0,6.44,7.89,0,16.28C657,198.05,660.19,205.38,651.6,210.75Z" opacity="0.2"/>
|
||||
|
||||
<path d="M659.11,241.88l-26.83,22.77c-4.3,3.45-6.44.85-6.44.85l-1.19-1.87-5.79-9c20.4-4.65,18.67-31.41,18.67-31.41h3.34a17.63,17.63,0,0,0,2.14,11.2c3.91-1.57,9-1.42,11.4-1.23a14.8,14.8,0,0,1,2.89.49C665.93,236,659.11,241.88,659.11,241.88Z" fill="#f9b499"/>
|
||||
|
||||
<path d="M659.11,241.88l-26.83,22.77c-4.3,3.45-6.44.85-6.44.85l-1.19-1.87c11.69-5.09,23-18.83,23-18.83,4.06-6-2.64-9.53-4.26-10.28a.16.16,0,0,1,0-.29c3.86-1.42,8.68-1.27,11-1.09a14.8,14.8,0,0,1,2.89.49C665.93,236,659.11,241.88,659.11,241.88Z" fill="#f7a48b"/>
|
||||
|
||||
<path d="M618.86,254.58l3.07,4.81s18.66-10.53,15-26.36C637,233,635.5,251.18,618.86,254.58Z" fill="#f7a48b"/>
|
||||
|
||||
<path d="M599,253.69a55.57,55.57,0,0,1,18.79,6.51" fill="none" stroke="red" stroke-miterlimit="10" stroke-width="0.75"/>
|
||||
|
||||
<path d="M710.77,332.4c0,8.24-5.5,8.24-5.5,8.24l-15,5.37c-6.68,2.23-9.44,1.89-10.5,1.46a1.62,1.62,0,0,0-1.36.1c.36-.24,1.68-1.46-.08-5l-5.63-8.42a1.13,1.13,0,0,1,.39-1.6,1.07,1.07,0,0,1,.55-.14,1.12,1.12,0,0,1,.91.46l7.14,9.93s1.07,4.29,15-2.15C696.69,340.64,708.75,336,710.77,332.4Z" opacity="0.2"/>
|
||||
|
||||
<path d="M674.48,349.38h0c-2.89.58-2.48-2.31-2.48-2.31a12.77,12.77,0,0,0,.2-1.54,9.91,9.91,0,0,0-5.8-9.37,26.59,26.59,0,0,0-4.77-1.68s8.22-3.51,12.51,7.22C674.14,341.71,675.46,346.35,674.48,349.38Z" opacity="0.2"/>
|
||||
|
||||
<path d="M677.36,323.46s-14,5.89-18.8,11l-8.33,1.91H608.39L570,335.27a98.24,98.24,0,0,1,12.58-65.48c.3,0,8.89,2.15,17.47,32.21,0,0,8.53,31.81,22,30.4h23.5s21.08.73,21.08-13.23V297.7s.65-8.42,4.09-3.13L681.66,317S683.8,321.32,677.36,323.46Z" opacity="0.2"/>
|
||||
|
||||
<path d="M680.58,258c-8.42,6.71-12.77,17.28-12.88,27.91-.1-1.33-.27-2.68-.25-4,0-9.45,4.89-19.05,13.13-23.89Z" opacity="0.2"/>
|
||||
|
||||
<path d="M640.87,324h0a.54.54,0,0,1-.52-.55l1.07-32.21c0-21.29,5.35-36.51,5.4-36.66a.54.54,0,0,1,.69-.32.53.53,0,0,1,.32.68c0,.15-5.33,15.2-5.33,36.32l-1.08,32.22A.54.54,0,0,1,640.87,324Z" fill="#282728" data-secondary="true"/>
|
||||
|
||||
<path d="M614,327.22h0a.54.54,0,0,1-.52-.55l1.08-31.13a208.17,208.17,0,0,1,2.69-33.9.53.53,0,0,1,.62-.43.54.54,0,0,1,.43.63,208.45,208.45,0,0,0-2.67,33.71l-1.07,31.15A.55.55,0,0,1,614,327.22Z" fill="#282728" data-secondary="true"/>
|
||||
|
||||
<g opacity="0.2">
|
||||
|
||||
<path d="M640.87,324h0a.54.54,0,0,1-.52-.55l1.07-32.21c0-21.29,5.35-36.51,5.4-36.66a.54.54,0,0,1,.69-.32.53.53,0,0,1,.32.68c0,.15-5.33,15.2-5.33,36.32l-1.08,32.22A.54.54,0,0,1,640.87,324Z"/>
|
||||
|
||||
<path d="M614,327.22h0a.54.54,0,0,1-.52-.55l1.08-31.13a208.17,208.17,0,0,1,2.69-33.9.53.53,0,0,1,.62-.43.54.54,0,0,1,.43.63,208.45,208.45,0,0,0-2.67,33.71l-1.07,31.15A.55.55,0,0,1,614,327.22Z"/>
|
||||
|
||||
</g>
|
||||
|
||||
<path d="M706.34,371.77c0,.17,0,8.76-2.14,25.76l-.34,2.43a58.67,58.67,0,0,0-.52,7.27c-.06,3.84-2.56,11.15-21.3,8.21.49-.83.94-1.65,1.36-2.49l.35-.68c1.57-3,3.69-7.58,4.35-11.52,0,0-59-5.36-78.37-12.88,0,0-28.3-11.81-38.84-8.05l-8.39,7s-7.51-2.15,3.22-15Z" fill="#787878" data-primary="true"/>
|
||||
|
||||
<path d="M688.1,400.75s13.8.92,13.29,7a4,4,0,0,1-1.61,2.85c-1.84,1.41-6.25,3.29-16.15,1.89A63,63,0,0,0,688.1,400.75Z" opacity="0.2"/>
|
||||
|
||||
<path d="M564.14,385.44s-6.33-4,6.75-5.62Z" opacity="0.2"/>
|
||||
|
||||
<path d="M688.1,400.75a53.84,53.84,0,0,1-4.35,11.52l-.35.68c-.23.43-.44.81-.63,1.15s-.47.89-.73,1.34c-13.3,23.42-41.56,44.44-53.06,52.37a12.19,12.19,0,0,0-5.27,9.22c-2.38,36.84-19.34,68.64-19.34,68.64-3.61,5.17-9,7.66-15,8.46-19.1,2.58-45.12-11.68-45.12-11.68l14-39.72c11.81-44,23.61-56.89,23.61-56.89l26.54-55.12,1.37-2.85C629.06,395.39,688.1,400.75,688.1,400.75Z" fill="#282728" data-secondary="true"/>
|
||||
|
||||
<path d="M688.1,400.75a53.84,53.84,0,0,1-4.35,11.52l-.35.68c-.23.43-.44.81-.63,1.15s-.47.89-.73,1.34c-13.3,23.42-41.56,44.44-53.06,52.37a12.19,12.19,0,0,0-5.27,9.22c-2.38,36.84-19.34,68.64-19.34,68.64-3.61,5.17-9,7.66-15,8.46,20-25.72,31.27-83,31.27-83-.89-13.33,38.47-47.86,38.47-47.86,15-15-4.29-19.33-4.29-19.33-17.27-2.72-39.92-10.84-46.46-13.25l1.37-2.85C629.06,395.39,688.1,400.75,688.1,400.75Z" opacity="0.2"/>
|
||||
|
||||
<path d="M589.37,430.17l-33.31,13.52,13,26s-5.54,16.19-11.4,35.76c0,0-13.42,9.12-27.37,8.05,0,0-11.81-30.06-18.25-61.19,0,0-8.59-18.25,6.44-30.06L562,387.05l8.93-7.23s6.64-4.83,38.84,8.05Z" fill="#282728" data-secondary="true"/>
|
||||
|
||||
<path d="M547.62,532.88,526.35,549.3a27.56,27.56,0,0,1-6.81,3.93c-3.21,1.25-7.85,4-6.84,9.17a8.45,8.45,0,0,0,5.07,6.09c2.55,1.06,6.59,1.78,12.53.37l11.81-9.23,7.47-14.54-5.33-2.64Z" fill="#787878" data-primary="true"/>
|
||||
|
||||
<path d="M547.62,532.88,526.35,549.3a27.56,27.56,0,0,1-6.81,3.93c-3.21,1.25-7.85,4-6.84,9.17a8.45,8.45,0,0,0,5.07,6.09c2.55,1.06,6.59,1.78,12.53.37l11.81-9.23,7.47-14.54-5.33-2.64Z" opacity="0.2"/>
|
||||
|
||||
<path d="M589.37,430.17l-33.31,13.52,13,26a370.36,370.36,0,0,1-11.89,36s-12.93,8.88-26.88,7.81c39.72-3.22,20.78-67.22,20.78-67.22-4.38-9.78,2.19-12.65,6.18-13.48a53.87,53.87,0,0,0,7.5-2.12l10.06-3.7a29.52,29.52,0,0,0,16.91-15.53,27.2,27.2,0,0,0,2.23-8.31,14.25,14.25,0,0,0-10.3-15c-8.42-2.48-16.86-1.84-21.7-1.1l8.93-7.23s6.64-4.83,38.84,8.05Z" opacity="0.2"/>
|
||||
|
||||
<path d="M512.56,561c8,4.49,17.84,3.92,26,.25,1.19-.51,2.33-1.14,3.54-1.66-1.08.73-2.18,1.45-3.3,2.13-8,4.53-18.78,5.1-26.25-.72Z" opacity="0.2"/>
|
||||
|
||||
<path d="M524.65,550.52c4.14,1.84,12.18,6.84,12.1,11.9-.72-2.81-3.3-4.59-5.35-6.46s-4.5-3.57-6.75-5.44Z" opacity="0.2"/>
|
||||
|
||||
<path d="M530.3,546.25c3.1.44,5.52,3.24,6.45,6.1-2.23-2.05-4-4.27-6.45-6.1Z" opacity="0.2"/>
|
||||
|
||||
<path d="M534.32,543.14a9.55,9.55,0,0,1,6.62,6.16c-2.17-2.19-4.35-4-6.62-6.16Z" opacity="0.2"/>
|
||||
|
||||
<path d="M538.89,539.61a11.81,11.81,0,0,1,5.36,6.61,29.31,29.31,0,0,1-5.36-6.61Z" opacity="0.2"/>
|
||||
|
||||
<rect x="421.88" y="364.26" width="328.48" height="7.51" fill="#b3b3b3"/>
|
||||
|
||||
<polygon points="750.36 364.26 750.36 371.77 786.86 342.79 786.88 336.37 750.36 364.26" fill="#999"/>
|
||||
|
||||
<path d="M507.76,344.93h98.07l-7.33-63.74a5.61,5.61,0,0,0-5.57-5h-90a3,3,0,0,0-2.93,3.31Z" fill="#787878" data-primary="true"/>
|
||||
|
||||
<path d="M605.83,344.93H507.76L500,279.54a3,3,0,0,1,2.95-3.31h90a5.61,5.61,0,0,1,5.56,5Z" fill="#fff" opacity="0.3"/>
|
||||
|
||||
<polygon points="583.53 276.23 507.76 341.71 506.12 329.04 567.52 276.23 583.53 276.23" fill="#fff" opacity="0.3"/>
|
||||
|
||||
<path d="M517.07,344.93l79.55-67.31a6,6,0,0,1,1.88,3.57l.38,3.34-71.09,60.4Z" fill="#fff" opacity="0.3"/>
|
||||
|
||||
<rect x="507.76" y="344.93" width="94.46" height="6.44" fill="#787878" data-primary="true"/>
|
||||
|
||||
<rect x="602.22" y="344.93" width="29.49" height="6.44" fill="#787878" data-primary="true"/>
|
||||
|
||||
<rect x="602.22" y="344.93" width="29.49" height="6.44" opacity="0.2"/>
|
||||
|
||||
<polygon points="419.73 353.52 466.38 353.52 499.97 333.94 459.85 333.94 419.73 353.52" fill="#fff"/>
|
||||
|
||||
<rect x="419.73" y="353.52" width="46.65" height="4.65" fill="#e6e6e6"/>
|
||||
|
||||
<polygon points="499.97 333.94 499.97 339.8 466.38 358.17 466.38 353.52 499.97 333.94" fill="#ccc"/>
|
||||
|
||||
<polygon points="499.97 333.94 499.97 339.8 466.38 358.17 466.38 353.52 499.97 333.94" opacity="0.1"/>
|
||||
|
||||
<path d="M658.56,334.46s-13.47,1.87-20.95,12.08c0,0-10.05,9.15-.18,7.53,0,0,.47,4.68,8.39,1.53,0,0,1.37,3.31,10-1.53,0,0,8.64-4.84,16.16-7C672,347.08,675.17,334.71,658.56,334.46Z" fill="#f9b499"/>
|
||||
|
||||
<path d="M646,343.86a40.12,40.12,0,0,1-8.55,10.21A40.49,40.49,0,0,1,646,343.86Z" fill="#f7a48b"/>
|
||||
|
||||
<path d="M645.82,355.6a24.61,24.61,0,0,1,6.85-7.82,24.71,24.71,0,0,1-6.85,7.82Z" fill="#f7a48b"/>
|
||||
|
||||
<ellipse cx="638.72" cy="215.58" rx="6.44" ry="8.05" fill="#f9b499"/>
|
||||
|
||||
<path d="M640.87,228.89s12.2-4.93,24.24-3.72a26.56,26.56,0,0,1,17.33,9.17c4.85,5.6,11.54,15.1,4.38,18.3a8.59,8.59,0,0,1-7.29-.33c-5-2.49-17.91-6.91-47.79,12.65l27.37-23.08s6.49-6.48-3.5-8.53a13.52,13.52,0,0,0-2.62-.25,46.27,46.27,0,0,0-10,1.27S640.87,230.93,640.87,228.89Z" fill="#787878" data-primary="true"/>
|
||||
|
||||
<path d="M640.87,228.89s12.2-4.93,24.24-3.72a26.56,26.56,0,0,1,17.33,9.17c4.85,5.6,11.54,15.1,4.38,18.3a8.59,8.59,0,0,1-7.29-.33c-5-2.49-17.91-6.91-47.79,12.65l27.37-23.08s6.49-6.48-3.5-8.53a13.52,13.52,0,0,0-2.62-.25,46.27,46.27,0,0,0-10,1.27S640.87,230.93,640.87,228.89Z" fill="#fff" opacity="0.3"/>
|
||||
|
||||
<path d="M674.14,234.37c-5.73,6.95-13.48,12.06-21.25,16.49-1.15.58-2.28,1.2-3.44,1.76,8.36-5.92,17-11.41,24.69-18.25Z" fill="#fff" opacity="0.3"/>
|
||||
|
||||
<path d="M683.8,238.66C679,244,671.85,246.84,664.89,248c6.47-2.57,13.26-5.24,18.91-9.35Z" fill="#fff" opacity="0.3"/>
|
||||
|
||||
<path d="M625.84,265.5c-4.44-2.67-21.36-6.8-27.08-8.15a23.81,23.81,0,0,0-3.37-.5c-4.81-.45-3.9-3.16-3.9-3.16,0-4.29,6.63-7.09,6.63-7.09,6,6.11,13.48,9.26,20.74,8Z" fill="#787878" data-primary="true"/>
|
||||
|
||||
<path d="M625.84,265.5c-4.44-2.67-21.36-6.8-27.08-8.15a23.81,23.81,0,0,0-3.37-.5c-4.81-.45-3.9-3.16-3.9-3.16,0-4.29,6.63-7.09,6.63-7.09,6,6.11,13.48,9.26,20.74,8Z" fill="#fff" opacity="0.3"/>
|
||||
|
||||
<circle cx="551.23" cy="311.12" r="8.05" fill="#fff"/>
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 23 KiB |
BIN
public/assets/forrest-gump-quote.webp
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
public/assets/logo.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
10
public/assets/logo.svg
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
public/astropaper-og.jpg
Normal file
After Width: | Height: | Size: 145 KiB |
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 4.2 KiB |
76
public/toggle-theme.js
Normal file
|
@ -0,0 +1,76 @@
|
|||
const primaryColorScheme = ""; // "light" | "dark"
|
||||
|
||||
// Get theme data from local storage
|
||||
const currentTheme = localStorage.getItem("theme");
|
||||
|
||||
function getPreferTheme() {
|
||||
// return theme value in local storage if it is set
|
||||
if (currentTheme) return currentTheme;
|
||||
|
||||
// return primary color scheme if it is set
|
||||
if (primaryColorScheme) return primaryColorScheme;
|
||||
|
||||
// return user device's prefer color scheme
|
||||
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? "dark"
|
||||
: "light";
|
||||
}
|
||||
|
||||
let themeValue = getPreferTheme();
|
||||
|
||||
function setPreference() {
|
||||
localStorage.setItem("theme", themeValue);
|
||||
reflectPreference();
|
||||
}
|
||||
|
||||
function reflectPreference() {
|
||||
document.firstElementChild.setAttribute("data-theme", themeValue);
|
||||
|
||||
document.querySelector("#theme-btn")?.setAttribute("aria-label", themeValue);
|
||||
|
||||
// Get a reference to the body element
|
||||
const body = document.body;
|
||||
|
||||
// Check if the body element exists before using getComputedStyle
|
||||
if (body) {
|
||||
// Get the computed styles for the body element
|
||||
const computedStyles = window.getComputedStyle(body);
|
||||
|
||||
// Get the background color property
|
||||
const bgColor = computedStyles.backgroundColor;
|
||||
|
||||
// Set the background color in <meta theme-color ... />
|
||||
document
|
||||
.querySelector("meta[name='theme-color']")
|
||||
?.setAttribute("content", bgColor);
|
||||
}
|
||||
}
|
||||
|
||||
// set early so no page flashes / CSS is made aware
|
||||
reflectPreference();
|
||||
|
||||
window.onload = () => {
|
||||
function setThemeFeature() {
|
||||
// set on load so screen readers can get the latest value on the button
|
||||
reflectPreference();
|
||||
|
||||
// now this script can find and listen for clicks on the control
|
||||
document.querySelector("#theme-btn")?.addEventListener("click", () => {
|
||||
themeValue = themeValue === "light" ? "dark" : "light";
|
||||
setPreference();
|
||||
});
|
||||
}
|
||||
|
||||
setThemeFeature();
|
||||
|
||||
// Runs on view transitions navigation
|
||||
document.addEventListener("astro:after-swap", setThemeFeature);
|
||||
};
|
||||
|
||||
// sync with system changes
|
||||
window
|
||||
.matchMedia("(prefers-color-scheme: dark)")
|
||||
.addEventListener("change", ({ matches: isDark }) => {
|
||||
themeValue = isDark ? "dark" : "light";
|
||||
setPreference();
|
||||
});
|
1
remark-collapse.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
declare module 'remark-collapse';
|
33
sidebars.js
|
@ -1,33 +0,0 @@
|
|||
/**
|
||||
* Creating a sidebar enables you to:
|
||||
- create an ordered group of docs
|
||||
- render a sidebar for each doc of that group
|
||||
- provide next/previous navigation
|
||||
|
||||
The sidebars can be generated from the filesystem, or explicitly defined here.
|
||||
|
||||
Create as many sidebars as you want.
|
||||
*/
|
||||
|
||||
// @ts-check
|
||||
|
||||
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
|
||||
const sidebars = {
|
||||
// By default, Docusaurus generates a sidebar from the docs folder structure
|
||||
tutorialSidebar: [{type: 'autogenerated', dirName: '.'}],
|
||||
|
||||
// But you can create a sidebar manually
|
||||
/*
|
||||
tutorialSidebar: [
|
||||
'intro',
|
||||
'hello',
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Tutorial',
|
||||
items: ['tutorial-basics/create-a-document'],
|
||||
},
|
||||
],
|
||||
*/
|
||||
};
|
||||
|
||||
module.exports = sidebars;
|
BIN
src/assets/images/Toastielab.png
Normal file
After Width: | Height: | Size: 124 KiB |
213
src/assets/socialIcons.ts
Normal file
|
@ -0,0 +1,213 @@
|
|||
import type { SocialIcons } from "../types";
|
||||
|
||||
const socialIcons: SocialIcons = {
|
||||
Github: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon-tabler"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path
|
||||
d="M9 19c-4.3 1.4 -4.3 -2.5 -6 -3m12 5v-3.5c0 -1 .1 -1.4 -.5 -2c2.8 -.3 5.5 -1.4 5.5 -6a4.6 4.6 0 0 0 -1.3 -3.2a4.2 4.2 0 0 0 -.1 -3.2s-1.1 -.3 -3.5 1.3a12.3 12.3 0 0 0 -6.2 0c-2.4 -1.6 -3.5 -1.3 -3.5 -1.3a4.2 4.2 0 0 0 -.1 3.2a4.6 4.6 0 0 0 -1.3 3.2c0 4.6 2.7 5.7 5.5 6c-.6 .6 -.6 1.2 -.5 2v3.5"
|
||||
></path>
|
||||
</svg>`,
|
||||
Facebook: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon-tabler"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path
|
||||
d="M7 10v4h3v7h4v-7h3l1 -4h-4v-2a1 1 0 0 1 1 -1h3v-4h-3a5 5 0 0 0 -5 5v2h-3"
|
||||
></path>
|
||||
</svg>`,
|
||||
Instagram: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon-tabler"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<rect x="4" y="4" width="16" height="16" rx="4"></rect>
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
<line x1="16.5" y1="7.5" x2="16.5" y2="7.501"></line>
|
||||
</svg>`,
|
||||
LinkedIn: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon-tabler"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<rect x="4" y="4" width="16" height="16" rx="2"></rect>
|
||||
<line x1="8" y1="11" x2="8" y2="16"></line>
|
||||
<line x1="8" y1="8" x2="8" y2="8.01"></line>
|
||||
<line x1="12" y1="16" x2="12" y2="11"></line>
|
||||
<path d="M16 16v-3a2 2 0 0 0 -4 0"></path>
|
||||
</svg>`,
|
||||
Mail: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon-tabler"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<rect x="3" y="5" width="18" height="14" rx="2"></rect>
|
||||
<polyline points="3 7 12 13 21 7"></polyline>
|
||||
</svg>`,
|
||||
Twitter: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon-tabler"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M22 4.01c-1 .49 -1.98 .689 -3 .99c-1.121 -1.265 -2.783 -1.335 -4.38 -.737s-2.643 2.06 -2.62 3.737v1c-3.245 .083 -6.135 -1.395 -8 -4c0 0 -4.182 7.433 4 11c-1.872 1.247 -3.739 2.088 -6 2c3.308 1.803 6.913 2.423 10.034 1.517c3.58 -1.04 6.522 -3.723 7.651 -7.742a13.84 13.84 0 0 0 .497 -3.753c-.002 -.249 1.51 -2.772 1.818 -4.013z"></path>
|
||||
</svg>`,
|
||||
Twitch: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon-tabler"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M21 2H3v16h5v4l4-4h5l4-4V2zm-10 9V7m5 4V7"></path>
|
||||
</svg>`,
|
||||
YouTube: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon-tabler"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M22.54 6.42a2.78 2.78 0 0 0-1.94-2C18.88 4 12 4 12 4s-6.88 0-8.6.46a2.78 2.78 0 0 0-1.94 2A29 29 0 0 0 1 11.75a29 29 0 0 0 .46 5.33A2.78 2.78 0 0 0 3.4 19c1.72.46 8.6.46 8.6.46s6.88 0 8.6-.46a2.78 2.78 0 0 0 1.94-2 29 29 0 0 0 .46-5.25 29 29 0 0 0-.46-5.33z"></path>
|
||||
<polygon points="9.75 15.02 15.5 11.75 9.75 8.48 9.75 15.02"></polygon>
|
||||
</svg>`,
|
||||
WhatsApp: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon-tabler"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M3 21l1.65 -3.8a9 9 0 1 1 3.4 2.9l-5.05 .9"></path>
|
||||
<path d="M9 10a0.5 .5 0 0 0 1 0v-1a0.5 .5 0 0 0 -1 0v1a5 5 0 0 0 5 5h1a0.5 .5 0 0 0 0 -1h-1a0.5 .5 0 0 0 0 1"></path>
|
||||
</svg>`,
|
||||
Snapchat: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon-tabler"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M16.882 7.842a4.882 4.882 0 0 0 -9.764 0c0 4.273 -.213 6.409 -4.118 8.118c2 .882 2 .882 3 3c3 0 4 2 6 2s3 -2 6 -2c1 -2.118 1 -2.118 3 -3c-3.906 -1.709 -4.118 -3.845 -4.118 -8.118zm-13.882 8.119c4 -2.118 4 -4.118 1 -7.118m17 7.118c-4 -2.118 -4 -4.118 -1 -7.118"></path>
|
||||
</svg>`,
|
||||
Pinterest: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon-tabler"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<line x1="8" y1="20" x2="12" y2="11"></line>
|
||||
<path d="M10.7 14c.437 1.263 1.43 2 2.55 2c2.071 0 3.75 -1.554 3.75 -4a5 5 0 1 0 -9.7 1.7"></path>
|
||||
<circle cx="12" cy="12" r="9"></circle>
|
||||
</svg>`,
|
||||
TikTok: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon-tabler"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M9 12a4 4 0 1 0 4 4v-12a5 5 0 0 0 5 5"></path>
|
||||
</svg>`,
|
||||
CodePen: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon-tabler"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M3 15l9 6l9 -6l-9 -6l-9 6"></path>
|
||||
<path d="M3 9l9 6l9 -6l-9 -6l-9 6"></path>
|
||||
<line x1="3" y1="9" x2="3" y2="15"></line>
|
||||
<line x1="21" y1="9" x2="21" y2="15"></line>
|
||||
<line x1="12" y1="3" x2="12" y2="9"></line>
|
||||
<line x1="12" y1="15" x2="12" y2="21"></line>
|
||||
</svg>`,
|
||||
Discord: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon-tabler"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<circle cx="9" cy="12" r="1"></circle>
|
||||
<circle cx="15" cy="12" r="1"></circle>
|
||||
<path d="M7.5 7.5c3.5 -1 5.5 -1 9 0"></path>
|
||||
<path d="M7 16.5c3.5 1 6.5 1 10 0"></path>
|
||||
<path d="M15.5 17c0 1 1.5 3 2 3c1.5 0 2.833 -1.667 3.5 -3c.667 -1.667 .5 -5.833 -1.5 -11.5c-1.457 -1.015 -3 -1.34 -4.5 -1.5l-1 2.5"></path>
|
||||
<path d="M8.5 17c0 1 -1.356 3 -1.832 3c-1.429 0 -2.698 -1.667 -3.333 -3c-.635 -1.667 -.476 -5.833 1.428 -11.5c1.388 -1.015 2.782 -1.34 4.237 -1.5l1 2.5"></path>
|
||||
</svg>`,
|
||||
GitLab: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon-tabler"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M21 14l-9 7l-9 -7l3 -11l3 7h6l3 -7z"></path>
|
||||
</svg>`,
|
||||
Reddit: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon-tabler"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M12 8c2.648 0 5.028 .826 6.675 2.14a2.5 2.5 0 0 1 2.326 4.36c0 3.59 -4.03 6.5 -9 6.5c-4.875 0 -8.845 -2.8 -9 -6.294l-1 -.206a2.5 2.5 0 0 1 2.326 -4.36c1.646 -1.313 4.026 -2.14 6.674 -2.14z"></path>
|
||||
<path d="M12 8l1 -5l6 1"></path>
|
||||
<circle cx="19" cy="4" r="1"></circle>
|
||||
<circle cx="9" cy="13" r=".5" fill="currentColor"></circle>
|
||||
<circle cx="15" cy="13" r=".5" fill="currentColor"></circle>
|
||||
<path d="M10 17c.667 .333 1.333 .5 2 .5s1.333 -.167 2 -.5"></path>
|
||||
</svg>`,
|
||||
Skype: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon-tabler"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M12 3a9 9 0 0 1 8.603 11.65a4.5 4.5 0 0 1 -5.953 5.953a9 9 0 0 1 -11.253 -11.253a4.5 4.5 0 0 1 5.953 -5.954a8.987 8.987 0 0 1 2.65 -.396z"></path>
|
||||
<path d="M8 14.5c.5 2 2.358 2.5 4 2.5c2.905 0 4 -1.187 4 -2.5c0 -1.503 -1.927 -2.5 -4 -2.5s-4 -.997 -4 -2.5c0 -1.313 1.095 -2.5 4 -2.5c1.642 0 3.5 .5 4 2.5"></path>
|
||||
</svg>`,
|
||||
Steam: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon-tabler"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M16.5 5a4.5 4.5 0 1 1 -.653 8.953l-4.347 3.009l0 .038a3 3 0 0 1 -2.824 2.995l-.176 .005a3 3 0 0 1 -2.94 -2.402l-2.56 -1.098v-3.5l3.51 1.755a2.989 2.989 0 0 1 2.834 -.635l2.727 -3.818a4.5 4.5 0 0 1 4.429 -5.302z"></path>
|
||||
<circle fill="currentColor" cx="16.5" cy="9.5" r="1"></circle>
|
||||
</svg>`,
|
||||
Telegram: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon-tabler"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M15 10l-4 4l6 6l4 -16l-18 7l4 2l2 6l3 -4"></path>
|
||||
</svg>`,
|
||||
Mastodon: `<svg class="icon-tabler" viewBox="-10 -5 1034 1034" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
|
||||
<path fill="currentColor"
|
||||
d="M499 112q-93 1 -166 11q-81 11 -128 33l-14 8q-16 10 -32 25q-22 21 -38 47q-21 33 -32 73q-14 47 -14 103v37q0 77 1 119q3 113 18 188q19 95 62 154q50 67 134 89q109 29 210 24q46 -3 88 -12q30 -7 55 -17l19 -8l-4 -75l-22 6q-28 6 -57 10q-41 6 -78 4q-53 -1 -80 -7
|
||||
q-43 -8 -67 -30q-29 -25 -35 -72q-2 -14 -2 -29l25 6q31 6 65 10q48 7 93 9q42 2 92 -2q32 -2 88 -9t107 -30q49 -23 81.5 -54.5t38.5 -63.5q9 -45 13 -109q4 -46 5 -97v-41q0 -56 -14 -103q-11 -40 -32 -73q-16 -26 -38 -47q-15 -15 -32 -25q-12 -8 -14 -8
|
||||
q-46 -22 -127 -33q-74 -10 -166 -11h-3zM367 267q73 0 109 56l24 39l24 -39q36 -56 109 -56q63 0 101 43t38 117v239h-95v-232q0 -74 -61 -74q-69 0 -69 88v127h-94v-127q0 -88 -69 -88q-61 0 -61 74v232h-95v-239q0 -74 38 -117t101 -43z" />
|
||||
</svg>`,
|
||||
};
|
||||
|
||||
export default socialIcons;
|
60
src/components/Breadcrumbs.astro
Normal file
|
@ -0,0 +1,60 @@
|
|||
---
|
||||
// Remove current url path and remove trailing slash if exists
|
||||
const currentUrlPath = Astro.url.pathname.replace(/\/+$/, "");
|
||||
|
||||
// Get url array from path
|
||||
// eg: /tags/tailwindcss => ['tags', 'tailwindcss']
|
||||
const breadcrumbList = currentUrlPath.split("/").slice(1);
|
||||
|
||||
// if breadcrumb is Home > Posts > 1 <etc>
|
||||
// replace Posts with Posts (page number)
|
||||
breadcrumbList[0] === "posts" &&
|
||||
breadcrumbList.splice(0, 2, `Posts (page ${breadcrumbList[1] || 1})`);
|
||||
---
|
||||
|
||||
<nav class="breadcrumb" aria-label="breadcrumb">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/">Home</a>
|
||||
<span aria-hidden="true"> > </span>
|
||||
</li>
|
||||
{
|
||||
breadcrumbList.map((breadcrumb, index) =>
|
||||
index + 1 === breadcrumbList.length ? (
|
||||
<li>
|
||||
<span
|
||||
class={`${index > 0 ? "lowercase" : "capitalize"}`}
|
||||
aria-current="page"
|
||||
>
|
||||
{/* make the last part lowercase in Home > Tags > some-tag */}
|
||||
{breadcrumb}
|
||||
</span>
|
||||
</li>
|
||||
) : (
|
||||
<li>
|
||||
<a href={`/${breadcrumb}`}>{breadcrumb}</a>
|
||||
<span aria-hidden="true"> > </span>
|
||||
</li>
|
||||
)
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
.breadcrumb {
|
||||
@apply mx-auto mb-1 mt-8 w-full max-w-3xl px-4;
|
||||
}
|
||||
.breadcrumb ul li {
|
||||
@apply inline;
|
||||
}
|
||||
.breadcrumb ul li a {
|
||||
@apply capitalize opacity-70;
|
||||
}
|
||||
.breadcrumb ul li span {
|
||||
@apply opacity-70;
|
||||
}
|
||||
.breadcrumb ul li:not(:last-child) a {
|
||||
@apply hover:opacity-100;
|
||||
}
|
||||
</style>
|
35
src/components/Card.tsx
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { slugifyStr } from "@utils/slugify";
|
||||
import Datetime from "./Datetime";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
export interface Props {
|
||||
href?: string;
|
||||
frontmatter: CollectionEntry<"blog">["data"];
|
||||
secHeading?: boolean;
|
||||
}
|
||||
|
||||
export default function Card({ href, frontmatter, secHeading = true }: Props) {
|
||||
const { title, pubDatetime, 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-skin-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 datetime={pubDatetime} />
|
||||
<p>{description}</p>
|
||||
</li>
|
||||
);
|
||||
}
|
52
src/components/Datetime.tsx
Normal file
|
@ -0,0 +1,52 @@
|
|||
import { LOCALE } from "@config";
|
||||
|
||||
export interface Props {
|
||||
datetime: string | Date;
|
||||
size?: "sm" | "lg";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function Datetime({ datetime, size = "sm", className }: Props) {
|
||||
return (
|
||||
<div className={`flex items-center space-x-2 opacity-80 ${className}`}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={`${
|
||||
size === "sm" ? "scale-90" : "scale-100"
|
||||
} inline-block h-6 w-6 fill-skin-base`}
|
||||
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>
|
||||
<span className="sr-only">Posted on:</span>
|
||||
<span className={`italic ${size === "sm" ? "text-sm" : "text-base"}`}>
|
||||
<FormattedDatetime datetime={datetime} />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const FormattedDatetime = ({ datetime }: { datetime: string | Date }) => {
|
||||
const myDatetime = new Date(datetime);
|
||||
|
||||
const date = myDatetime.toLocaleDateString(LOCALE, {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
});
|
||||
|
||||
const time = myDatetime.toLocaleTimeString(LOCALE, {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{date}
|
||||
<span aria-hidden="true"> | </span>
|
||||
<span className="sr-only"> at </span>
|
||||
{time}
|
||||
</>
|
||||
);
|
||||
};
|
43
src/components/Footer.astro
Normal file
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
import Hr from "./Hr.astro";
|
||||
import Socials from "./Socials.astro";
|
||||
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
export interface Props {
|
||||
noMarginTop?: boolean;
|
||||
}
|
||||
|
||||
const { noMarginTop = false } = Astro.props;
|
||||
---
|
||||
|
||||
<footer class={`${noMarginTop ? "" : "mt-auto"}`}>
|
||||
<Hr noPadding />
|
||||
<div class="footer-wrapper">
|
||||
<Socials centered />
|
||||
<div class="copyright-wrapper">
|
||||
<span>Copyright © {currentYear}, toastie_t0ast</span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<style>
|
||||
footer {
|
||||
@apply w-full;
|
||||
}
|
||||
.footer-wrapper {
|
||||
@apply flex flex-col items-center justify-between py-6 sm:flex-row-reverse sm:py-4;
|
||||
}
|
||||
.link-button {
|
||||
@apply my-1 p-2 hover:rotate-6;
|
||||
}
|
||||
.link-button svg {
|
||||
@apply scale-125;
|
||||
}
|
||||
.copyright-wrapper {
|
||||
@apply my-2 flex flex-col items-center whitespace-nowrap sm:flex-row;
|
||||
}
|
||||
.separator {
|
||||
@apply hidden sm:inline;
|
||||
}
|
||||
</style>
|
214
src/components/Header.astro
Normal file
|
@ -0,0 +1,214 @@
|
|||
---
|
||||
import { LOGO_IMAGE, SITE } from "@config";
|
||||
import Hr from "./Hr.astro";
|
||||
import LinkButton from "./LinkButton.astro";
|
||||
|
||||
export interface Props {
|
||||
activeNav?: "posts" | "tags" | "about" | "search";
|
||||
}
|
||||
|
||||
const { activeNav } = Astro.props;
|
||||
---
|
||||
|
||||
<header>
|
||||
<a id="skip-to-content" href="#main-content">Skip to content</a>
|
||||
<div class="nav-container">
|
||||
<div class="top-nav-wrap">
|
||||
<a href="/" class="logo whitespace-nowrap">
|
||||
{
|
||||
LOGO_IMAGE.enable ? (
|
||||
<img
|
||||
src={`/assets/${LOGO_IMAGE.svg ? "logo.svg" : "logo.png"}`}
|
||||
alt={SITE.title}
|
||||
width={LOGO_IMAGE.width}
|
||||
height={LOGO_IMAGE.height}
|
||||
/>
|
||||
) : (
|
||||
SITE.title
|
||||
)
|
||||
}
|
||||
</a>
|
||||
<nav id="nav-menu">
|
||||
<button
|
||||
class="hamburger-menu focus-outline"
|
||||
aria-label="Open Menu"
|
||||
aria-expanded="false"
|
||||
aria-controls="menu-items"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="menu-icon"
|
||||
>
|
||||
<line x1="7" y1="12" x2="21" y2="12" class="line"></line>
|
||||
<line x1="3" y1="6" x2="21" y2="6" class="line"></line>
|
||||
<line x1="12" y1="18" x2="21" y2="18" class="line"></line>
|
||||
<line x1="18" y1="6" x2="6" y2="18" class="close"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18" class="close"></line>
|
||||
</svg>
|
||||
</button>
|
||||
<ul id="menu-items" class="display-none sm:flex">
|
||||
<li>
|
||||
<a href="/posts" class={activeNav === "posts" ? "active" : ""}>
|
||||
Posts
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/tags" class={activeNav === "tags" ? "active" : ""}>
|
||||
Tags
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/about" class={activeNav === "about" ? "active" : ""}>
|
||||
About
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<LinkButton
|
||||
href="/search"
|
||||
className={`focus-outline p-3 sm:p-1 ${
|
||||
activeNav === "search" ? "active" : ""
|
||||
} flex`}
|
||||
ariaLabel="search"
|
||||
title="Search"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="scale-125 sm:scale-100"
|
||||
><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>
|
||||
</LinkButton>
|
||||
</li>
|
||||
<li>
|
||||
{
|
||||
SITE.lightAndDarkMode && (
|
||||
<button
|
||||
id="theme-btn"
|
||||
class="focus-outline"
|
||||
title="Toggles light & dark"
|
||||
aria-label="auto"
|
||||
aria-live="polite"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="moon-svg">
|
||||
<path d="M20.742 13.045a8.088 8.088 0 0 1-2.077.271c-2.135 0-4.14-.83-5.646-2.336a8.025 8.025 0 0 1-2.064-7.723A1 1 0 0 0 9.73 2.034a10.014 10.014 0 0 0-4.489 2.582c-3.898 3.898-3.898 10.243 0 14.143a9.937 9.937 0 0 0 7.072 2.93 9.93 9.93 0 0 0 7.07-2.929 10.007 10.007 0 0 0 2.583-4.491 1.001 1.001 0 0 0-1.224-1.224zm-2.772 4.301a7.947 7.947 0 0 1-5.656 2.343 7.953 7.953 0 0 1-5.658-2.344c-3.118-3.119-3.118-8.195 0-11.314a7.923 7.923 0 0 1 2.06-1.483 10.027 10.027 0 0 0 2.89 7.848 9.972 9.972 0 0 0 7.848 2.891 8.036 8.036 0 0 1-1.484 2.059z" />
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="sun-svg">
|
||||
<path d="M6.993 12c0 2.761 2.246 5.007 5.007 5.007s5.007-2.246 5.007-5.007S14.761 6.993 12 6.993 6.993 9.239 6.993 12zM12 8.993c1.658 0 3.007 1.349 3.007 3.007S13.658 15.007 12 15.007 8.993 13.658 8.993 12 10.342 8.993 12 8.993zM10.998 19h2v3h-2zm0-17h2v3h-2zm-9 9h3v2h-3zm17 0h3v2h-3zM4.219 18.363l2.12-2.122 1.415 1.414-2.12 2.122zM16.24 6.344l2.122-2.122 1.414 1.414-2.122 2.122zM6.342 7.759 4.22 5.637l1.415-1.414 2.12 2.122zm13.434 10.605-1.414 1.414-2.122-2.122 1.414-1.414z" />
|
||||
</svg>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<Hr />
|
||||
</header>
|
||||
|
||||
<style>
|
||||
#skip-to-content {
|
||||
@apply absolute -top-full left-16 z-50 bg-skin-accent px-3 py-2 text-skin-inverted transition-all focus:top-4;
|
||||
}
|
||||
.nav-container {
|
||||
@apply mx-auto flex max-w-3xl flex-col items-center justify-between sm:flex-row;
|
||||
}
|
||||
.top-nav-wrap {
|
||||
@apply relative flex w-full items-start justify-between p-4 sm:items-center sm:py-8;
|
||||
}
|
||||
.logo {
|
||||
@apply absolute py-1 text-xl font-semibold sm:static sm:text-2xl;
|
||||
}
|
||||
.hamburger-menu {
|
||||
@apply self-end p-2 sm:hidden;
|
||||
}
|
||||
.hamburger-menu svg {
|
||||
@apply h-6 w-6 scale-125 fill-skin-base;
|
||||
}
|
||||
|
||||
nav {
|
||||
@apply flex w-full flex-col items-center sm:ml-2 sm:flex-row sm:justify-end sm:space-x-4 sm:py-0;
|
||||
}
|
||||
nav ul {
|
||||
@apply mt-4 grid w-44 grid-cols-2 grid-rows-4 gap-x-2 gap-y-2 sm:ml-0 sm:mt-0 sm:w-auto sm:gap-x-5 sm:gap-y-0;
|
||||
}
|
||||
nav ul li {
|
||||
@apply col-span-2 flex items-center justify-center;
|
||||
}
|
||||
nav ul li a {
|
||||
@apply w-full px-4 py-3 text-center font-medium hover:text-skin-accent sm:my-0 sm:px-2 sm:py-1;
|
||||
}
|
||||
nav ul li:nth-last-child(2) a {
|
||||
@apply w-auto;
|
||||
}
|
||||
nav ul li:nth-last-child(1),
|
||||
nav ul li:nth-last-child(2) {
|
||||
@apply col-span-1;
|
||||
}
|
||||
nav a.active {
|
||||
@apply underline decoration-wavy decoration-2 underline-offset-4;
|
||||
}
|
||||
nav a.active svg {
|
||||
@apply fill-skin-accent;
|
||||
}
|
||||
|
||||
nav button {
|
||||
@apply p-1;
|
||||
}
|
||||
nav button svg {
|
||||
@apply h-6 w-6 fill-skin-base hover:fill-skin-accent;
|
||||
}
|
||||
#theme-btn {
|
||||
@apply p-3 sm:p-1;
|
||||
}
|
||||
#theme-btn svg {
|
||||
@apply scale-125 hover:rotate-12 sm:scale-100;
|
||||
}
|
||||
|
||||
.menu-icon line {
|
||||
@apply transition-opacity duration-75 ease-in-out;
|
||||
}
|
||||
.menu-icon .close {
|
||||
opacity: 0;
|
||||
}
|
||||
.menu-icon.is-active .line {
|
||||
@apply opacity-0;
|
||||
}
|
||||
.menu-icon.is-active .close {
|
||||
@apply opacity-100;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function toggleNav() {
|
||||
// Toggle menu
|
||||
const menuBtn = document.querySelector(".hamburger-menu");
|
||||
const menuIcon = document.querySelector(".menu-icon");
|
||||
const menuItems = document.querySelector("#menu-items");
|
||||
|
||||
menuBtn?.addEventListener("click", () => {
|
||||
const menuExpanded = menuBtn.getAttribute("aria-expanded") === "true";
|
||||
menuIcon?.classList.toggle("is-active");
|
||||
menuBtn.setAttribute("aria-expanded", menuExpanded ? "false" : "true");
|
||||
menuBtn.setAttribute(
|
||||
"aria-label",
|
||||
menuExpanded ? "Open Menu" : "Close Menu"
|
||||
);
|
||||
menuItems?.classList.toggle("display-none");
|
||||
});
|
||||
}
|
||||
|
||||
toggleNav();
|
||||
|
||||
// Runs on view transitions navigation
|
||||
document.addEventListener("astro:after-swap", toggleNav);
|
||||
</script>
|
|
@ -1,64 +0,0 @@
|
|||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
const FeatureList = [
|
||||
{
|
||||
title: 'Easy to Use',
|
||||
Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
|
||||
description: (
|
||||
<>
|
||||
Docusaurus was designed from the ground up to be easily installed and
|
||||
used to get your website up and running quickly.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Focus on What Matters',
|
||||
Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default,
|
||||
description: (
|
||||
<>
|
||||
Docusaurus lets you focus on your docs, and we'll do the chores. Go
|
||||
ahead and move your docs into the <code>docs</code> directory.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Powered by React',
|
||||
Svg: require('@site/static/img/undraw_docusaurus_react.svg').default,
|
||||
description: (
|
||||
<>
|
||||
Extend or customize your website layout by reusing React. Docusaurus can
|
||||
be extended while reusing the same header and footer.
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
function Feature({Svg, title, description}) {
|
||||
return (
|
||||
<div className={clsx('col col--4')}>
|
||||
<div className="text--center">
|
||||
<Svg className={styles.featureSvg} role="img" />
|
||||
</div>
|
||||
<div className="text--center padding-horiz--md">
|
||||
<h3>{title}</h3>
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function HomepageFeatures() {
|
||||
return (
|
||||
<section className={styles.features}>
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
{FeatureList.map((props, idx) => (
|
||||
<Feature key={idx} {...props} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
.features {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 2rem 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.featureSvg {
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
}
|
12
src/components/Hr.astro
Normal file
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
export interface Props {
|
||||
noPadding?: boolean;
|
||||
ariaHidden?: boolean;
|
||||
}
|
||||
|
||||
const { noPadding = false, ariaHidden = true } = Astro.props;
|
||||
---
|
||||
|
||||
<div class={`max-w-3xl mx-auto ${noPadding ? "px-0" : "px-4"}`}>
|
||||
<hr class="border-skin-line" aria-hidden={ariaHidden} />
|
||||
</div>
|
28
src/components/LinkButton.astro
Normal file
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
export interface Props {
|
||||
href: string;
|
||||
className?: string;
|
||||
ariaLabel?: string;
|
||||
title?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const { href, className, ariaLabel, title, disabled = false } = Astro.props;
|
||||
---
|
||||
|
||||
<a
|
||||
href={disabled ? "#" : href}
|
||||
tabindex={disabled ? "-1" : "0"}
|
||||
class={`group inline-block ${className}`}
|
||||
aria-label={ariaLabel}
|
||||
title={title}
|
||||
aria-disabled={disabled}
|
||||
>
|
||||
<slot />
|
||||
</a>
|
||||
|
||||
<style>
|
||||
a {
|
||||
@apply hover:text-skin-accent;
|
||||
}
|
||||
</style>
|
122
src/components/Search.tsx
Normal file
|
@ -0,0 +1,122 @@
|
|||
import Fuse from "fuse.js";
|
||||
import { useEffect, useRef, useState, useMemo } from "react";
|
||||
import Card from "@components/Card";
|
||||
import slugify from "@utils/slugify";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
export type SearchItem = {
|
||||
title: string;
|
||||
description: string;
|
||||
data: CollectionEntry<"blog">["data"];
|
||||
};
|
||||
|
||||
interface Props {
|
||||
searchList: SearchItem[];
|
||||
}
|
||||
|
||||
interface SearchResult {
|
||||
item: SearchItem;
|
||||
refIndex: number;
|
||||
}
|
||||
|
||||
export default function SearchBar({ searchList }: Props) {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [inputVal, setInputVal] = useState("");
|
||||
const [searchResults, setSearchResults] = useState<SearchResult[] | null>(
|
||||
null
|
||||
);
|
||||
|
||||
const handleChange = (e: React.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);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// Add search result only if
|
||||
// input value is more than one character
|
||||
let 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);
|
||||
const newRelativePathQuery =
|
||||
window.location.pathname + "?" + searchParams.toString();
|
||||
history.replaceState(history.state, "", newRelativePathQuery);
|
||||
} else {
|
||||
history.replaceState(history.state, "", window.location.pathname);
|
||||
}
|
||||
}, [inputVal]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<label className="relative block">
|
||||
<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">
|
||||
<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="block w-full rounded border border-skin-fill
|
||||
border-opacity-40 bg-skin-fill py-3 pl-10
|
||||
pr-3 placeholder:italic placeholder:text-opacity-75
|
||||
focus:border-skin-accent 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/${slugify(item.data)}`}
|
||||
frontmatter={item.data}
|
||||
key={`${refIndex}-${slugify(item.data)}`}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
}
|
34
src/components/Socials.astro
Normal file
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
import { SOCIALS } from "@config";
|
||||
import LinkButton from "./LinkButton.astro";
|
||||
import socialIcons from "@assets/socialIcons";
|
||||
|
||||
export interface Props {
|
||||
centered?: boolean;
|
||||
}
|
||||
|
||||
const { centered = false } = Astro.props;
|
||||
---
|
||||
|
||||
<div class={`social-icons ${centered ? "flex" : ""}`}>
|
||||
{
|
||||
SOCIALS.filter(social => social.active).map(social => (
|
||||
<LinkButton
|
||||
href={social.href}
|
||||
className="link-button"
|
||||
title={social.linkTitle}
|
||||
>
|
||||
<Fragment set:html={socialIcons[social.name]} />
|
||||
</LinkButton>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.social-icons {
|
||||
@apply flex-wrap justify-center gap-1;
|
||||
}
|
||||
.link-button {
|
||||
@apply p-2 hover:rotate-6 sm:p-1;
|
||||
}
|
||||
</style>
|
38
src/components/Tag.astro
Normal file
|
@ -0,0 +1,38 @@
|
|||
---
|
||||
export interface Props {
|
||||
name: string;
|
||||
size?: "sm" | "lg";
|
||||
}
|
||||
|
||||
const { name, size = "sm" } = Astro.props;
|
||||
---
|
||||
|
||||
<li
|
||||
class={`inline-block ${
|
||||
size === "sm" ? "my-1 underline-offset-4" : "my-3 mx-1 underline-offset-8"
|
||||
}`}
|
||||
>
|
||||
<a
|
||||
href={`/tags/${name.toLowerCase()}`}
|
||||
transition:name={name.toLowerCase()}
|
||||
class={`${size === "sm" ? "text-sm" : "text-lg"} pr-2 group`}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class={`${size === "sm" ? " scale-75" : "scale-110"}`}
|
||||
><path
|
||||
d="M16.018 3.815 15.232 8h-4.966l.716-3.815-1.964-.37L8.232 8H4v2h3.857l-.751 4H3v2h3.731l-.714 3.805 1.965.369L8.766 16h4.966l-.714 3.805 1.965.369.783-4.174H20v-2h-3.859l.751-4H21V8h-3.733l.716-3.815-1.965-.37zM14.106 14H9.141l.751-4h4.966l-.752 4z"
|
||||
></path>
|
||||
</svg>
|
||||
<span>{name.toLowerCase()}</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<style>
|
||||
a {
|
||||
@apply relative underline decoration-dashed hover:-top-0.5 hover:text-skin-accent focus-visible:p-1;
|
||||
}
|
||||
a svg {
|
||||
@apply -mr-5 h-6 w-6 scale-95 text-skin-base opacity-80 group-hover:fill-skin-accent;
|
||||
}
|
||||
</style>
|
53
src/config.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
import type { Site, SocialObjects } from "./types";
|
||||
|
||||
export const SITE: Site = {
|
||||
website: "https://blog.toastiet0ast.com/", // replace this with your deployed domain
|
||||
author: "Toastie",
|
||||
desc: "The ramblins of a random software developer.",
|
||||
title: "Toastie's blog",
|
||||
ogImage: "",
|
||||
lightAndDarkMode: true,
|
||||
postPerPage: 3,
|
||||
};
|
||||
|
||||
export const LOCALE = ["en-EN"]; // set to [] to use the environment default
|
||||
|
||||
export const LOGO_IMAGE = {
|
||||
enable: false,
|
||||
svg: true,
|
||||
width: 216,
|
||||
height: 46,
|
||||
};
|
||||
|
||||
export const SOCIALS: SocialObjects = [
|
||||
{
|
||||
name: "Mail",
|
||||
href: "mailto:toastie@toastiet0ast.com",
|
||||
linkTitle: `Send an email to me`,
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
name: "Twitch",
|
||||
href: "https://twitch.tv/toastie_t0ast",
|
||||
linkTitle: `My channel on Twitch`,
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
name: "YouTube",
|
||||
href: "https://youtube.com/@toastie_t0ast",
|
||||
linkTitle: `My YouTube channel`,
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
name: "Discord",
|
||||
href: "https://discord.gg/aAsGMFPfdu",
|
||||
linkTitle: `My Discord server`,
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
name: "Mastodon",
|
||||
href: "https://valkyriecoms.com/@toastie",
|
||||
linkTitle: `My profile on Valkyriecoms`,
|
||||
active: true,
|
||||
},
|
||||
];
|
BIN
src/content/blog/image.png
Normal file
After Width: | Height: | Size: 118 KiB |
41
src/content/blog/new-things.md
Normal file
|
@ -0,0 +1,41 @@
|
|||
---
|
||||
author: Toastie
|
||||
pubDatetime: 2023-12-09
|
||||
title: New projects
|
||||
description: The projects I have started.
|
||||
tags:
|
||||
- project-update
|
||||
featured: true
|
||||
---
|
||||
|
||||
Hi there, it has been a while.
|
||||
|
||||
I am back to let you all know how life has been treating me so far as well as share the projects I have been working on.
|
||||
|
||||
### First up is Valkyriecoms,
|
||||
|
||||
Valkyriecoms is a social site I have been working on since December last year and just got things fully up, below is a screenshot of the site.
|
||||
|
||||
![](https://cdn.discordapp.com/attachments/1138770664342441984/1182117160609386650/image.png)
|
||||
|
||||
### Next up is Dragon's child hosting
|
||||
|
||||
Dragon's child hosting is a small server hosting service I started under the Dragon's child studios name right now we are just starting out on our journey.
|
||||
|
||||
Below is a screenshot of our panel (there is a new one which will replace this in the works)
|
||||
|
||||
![](https://cdn.discordapp.com/attachments/881396607218753607/1182651077384994816/dashboard.png)
|
||||
|
||||
### Last up is Toastielab
|
||||
|
||||
Toastielab is a small git platform that I operate wit the help of Dragon's child studios which is where I host all my projects (even the one which this site is located at)
|
||||
|
||||
I am going to include a screenshot of Toastielab below (our icons a little bit bugged right now so it is currently showing the default Forgejo icons)
|
||||
|
||||
![](https://cdn.discordapp.com/attachments/881396607218753607/1182653249480835163/image.png)
|
||||
|
||||
### Here is a list of links for the projects listed above
|
||||
|
||||
- https://valkyriecoms.com
|
||||
- https://dragonschildhosting.net
|
||||
- https://toastielab.dev
|
|
@ -1,8 +1,9 @@
|
|||
---
|
||||
slug: emotionchild-update
|
||||
pubDatetime: 2023-06-21
|
||||
title: Update on things
|
||||
authors: [toastie_t0ast]
|
||||
tags: [Update]
|
||||
description: Where I have been among other things.
|
||||
tags:
|
||||
- update
|
||||
---
|
||||
|
||||
Hey guys it has been a while and I have not streamed in a little while and I want to update you on why this has happened, First of all I have been having somputer issues as well as some IRL stuff taking over a lot, Secondly I have started some major projects, one with a friend and another to support me first project. These projects are as follows [Valkyriecoms](https://valkyriecoms.com), [Toastielab](https://toastielab.dev) and last but not least a small group stared by me and a good friend who you may know by Elearu called [Dragon's Child Studios](https://dragonschildstudios.com).
|
||||
|
@ -11,4 +12,4 @@ As you can see I have been quite busy but I hope I will be able to return to str
|
|||
|
||||
Thanks for taking the time to read this,
|
||||
|
||||
Toastie_t0ast (EmotionChild)
|
||||
Toastie_t0ast
|
26
src/content/config.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import { SITE } from "@config";
|
||||
import { defineCollection, z } from "astro:content";
|
||||
|
||||
const blog = defineCollection({
|
||||
type: "content",
|
||||
schema: ({ image }) =>
|
||||
z.object({
|
||||
author: z.string().default(SITE.author),
|
||||
pubDatetime: z.date(),
|
||||
title: z.string(),
|
||||
postSlug: z.string().optional(),
|
||||
featured: z.boolean().optional(),
|
||||
draft: z.boolean().optional(),
|
||||
tags: z.array(z.string()).default(["others"]),
|
||||
ogImage: image()
|
||||
.refine(img => img.width >= 1200 && img.height >= 630, {
|
||||
message: "OpenGraph image must be at least 1200 X 630 pixels!",
|
||||
})
|
||||
.or(z.string())
|
||||
.optional(),
|
||||
description: z.string(),
|
||||
canonicalURL: z.string().optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const collections = { blog };
|
|
@ -1,30 +0,0 @@
|
|||
/**
|
||||
* Any CSS included here will be global. The classic template
|
||||
* bundles Infima by default. Infima is a CSS framework designed to
|
||||
* work well for content-centric websites.
|
||||
*/
|
||||
|
||||
/* You can override the default Infima variables here. */
|
||||
:root {
|
||||
--ifm-color-primary: #2e8555;
|
||||
--ifm-color-primary-dark: #29784c;
|
||||
--ifm-color-primary-darker: #277148;
|
||||
--ifm-color-primary-darkest: #205d3b;
|
||||
--ifm-color-primary-light: #33925d;
|
||||
--ifm-color-primary-lighter: #359962;
|
||||
--ifm-color-primary-lightest: #3cad6e;
|
||||
--ifm-code-font-size: 95%;
|
||||
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* For readability concerns, you should choose a lighter palette in dark mode. */
|
||||
[data-theme='dark'] {
|
||||
--ifm-color-primary: #25c2a0;
|
||||
--ifm-color-primary-dark: #21af90;
|
||||
--ifm-color-primary-darker: #1fa588;
|
||||
--ifm-color-primary-darkest: #1a8870;
|
||||
--ifm-color-primary-light: #29d5b0;
|
||||
--ifm-color-primary-lighter: #32d8b4;
|
||||
--ifm-color-primary-lightest: #4fddbf;
|
||||
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
|
||||
}
|
2
src/env.d.ts
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/// <reference path="../.astro/types.d.ts" />
|
||||
/// <reference types="astro/client" />
|
28
src/layouts/AboutLayout.astro
Normal file
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
import { SITE } from "@config";
|
||||
import Breadcrumbs from "@components/Breadcrumbs.astro";
|
||||
import Footer from "@components/Footer.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
import Layout from "./Layout.astro";
|
||||
|
||||
export interface Props {
|
||||
frontmatter: {
|
||||
title: string;
|
||||
description?: string;
|
||||
};
|
||||
}
|
||||
|
||||
const { frontmatter } = Astro.props;
|
||||
---
|
||||
|
||||
<Layout title={`${frontmatter.title} | ${SITE.title}`}>
|
||||
<Header activeNav="about" />
|
||||
<Breadcrumbs />
|
||||
<main id="main-content">
|
||||
<section id="about" class="prose mb-28 max-w-3xl prose-img:border-0">
|
||||
<h1 class="text-2xl tracking-wider sm:text-3xl">{frontmatter.title}</h1>
|
||||
<slot />
|
||||
</section>
|
||||
</main>
|
||||
<Footer />
|
||||
</Layout>
|
88
src/layouts/Layout.astro
Normal file
|
@ -0,0 +1,88 @@
|
|||
---
|
||||
import { SITE } from "@config";
|
||||
import "@styles/base.css";
|
||||
import { ViewTransitions } from "astro:transitions";
|
||||
|
||||
const googleSiteVerification = import.meta.env.PUBLIC_GOOGLE_SITE_VERIFICATION;
|
||||
|
||||
export interface Props {
|
||||
title?: string;
|
||||
author?: string;
|
||||
description?: string;
|
||||
ogImage?: string;
|
||||
canonicalURL?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
title = SITE.title,
|
||||
author = SITE.author,
|
||||
description = SITE.desc,
|
||||
ogImage = SITE.ogImage,
|
||||
canonicalURL = new URL(Astro.url.pathname, Astro.site).href,
|
||||
} = Astro.props;
|
||||
|
||||
const socialImageURL = new URL(
|
||||
ogImage ?? SITE.ogImage ?? "og.png",
|
||||
Astro.url.origin
|
||||
).href;
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="canonical" href={canonicalURL} />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
|
||||
<!-- General Meta Tags -->
|
||||
<title>{title}</title>
|
||||
<meta name="title" content={title} />
|
||||
<meta name="description" content={description} />
|
||||
<meta name="author" content={author} />
|
||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:url" content={canonicalURL} />
|
||||
<meta property="og:image" content={socialImageURL} />
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:url" content={canonicalURL} />
|
||||
<meta property="twitter:title" content={title} />
|
||||
<meta property="twitter:description" content={description} />
|
||||
<meta property="twitter:image" content={socialImageURL} />
|
||||
|
||||
<!-- Google Font -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,600;0,700;1,400;1,600&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<meta name="theme-color" content="" />
|
||||
|
||||
{
|
||||
// If PUBLIC_GOOGLE_SITE_VERIFICATION is set in the environment variable,
|
||||
// include google-site-verification tag in the heading
|
||||
// Learn more: https://support.google.com/webmasters/answer/9008080#meta_tag_verification&zippy=%2Chtml-tag
|
||||
googleSiteVerification && (
|
||||
<meta
|
||||
name="google-site-verification"
|
||||
content={googleSiteVerification}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
<ViewTransitions />
|
||||
|
||||
<script is:inline src="/toggle-theme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<slot />
|
||||
</body>
|
||||
</html>
|
48
src/layouts/Main.astro
Normal file
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
import Breadcrumbs from "@components/Breadcrumbs.astro";
|
||||
|
||||
interface StringTitleProp {
|
||||
pageTitle: string;
|
||||
pageDesc?: string;
|
||||
}
|
||||
|
||||
interface ArrayTitleProp {
|
||||
pageTitle: [string, string];
|
||||
titleTransition: string;
|
||||
pageDesc?: string;
|
||||
}
|
||||
|
||||
export type Props = StringTitleProp | ArrayTitleProp;
|
||||
|
||||
const { props } = Astro;
|
||||
---
|
||||
|
||||
<Breadcrumbs />
|
||||
<main id="main-content">
|
||||
{
|
||||
"titleTransition" in props ? (
|
||||
<h1>
|
||||
{props.pageTitle[0]}
|
||||
<span transition:name={props.titleTransition}>
|
||||
{props.pageTitle[1]}
|
||||
</span>
|
||||
</h1>
|
||||
) : (
|
||||
<h1>{props.pageTitle}</h1>
|
||||
)
|
||||
}
|
||||
<p>{props.pageDesc}</p>
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<style>
|
||||
#main-content {
|
||||
@apply mx-auto w-full max-w-3xl px-4 pb-12;
|
||||
}
|
||||
#main-content h1 {
|
||||
@apply text-2xl font-semibold sm:text-3xl;
|
||||
}
|
||||
#main-content p {
|
||||
@apply mb-6 mt-2 italic;
|
||||
}
|
||||
</style>
|
72
src/layouts/PostDetails.astro
Normal file
|
@ -0,0 +1,72 @@
|
|||
---
|
||||
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 type { CollectionEntry } from "astro:content";
|
||||
import { slugifyStr } from "@utils/slugify";
|
||||
|
||||
export interface Props {
|
||||
post: CollectionEntry<"blog">;
|
||||
}
|
||||
|
||||
const { post } = Astro.props;
|
||||
|
||||
const { title, author, description, ogImage, canonicalURL, pubDatetime, tags } =
|
||||
post.data;
|
||||
|
||||
const { Content } = await post.render();
|
||||
|
||||
const ogImageUrl = typeof ogImage === "string" ? ogImage : ogImage?.src;
|
||||
const ogUrl = new URL(
|
||||
ogImageUrl ?? `/posts/${slugifyStr(title)}.png`,
|
||||
Astro.url.origin
|
||||
).href;
|
||||
---
|
||||
|
||||
<Layout
|
||||
title={title}
|
||||
author={author}
|
||||
description={description}
|
||||
ogImage={ogUrl}
|
||||
canonicalURL={canonicalURL}
|
||||
>
|
||||
<Header />
|
||||
<div class="mx-auto flex w-full max-w-3xl justify-start px-2">
|
||||
<button
|
||||
class="focus-outline mb-2 mt-8 flex hover:opacity-75"
|
||||
onclick="history.back()"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
><path
|
||||
d="M13.293 6.293 7.586 12l5.707 5.707 1.414-1.414L10.414 12l4.293-4.293z"
|
||||
></path>
|
||||
</svg><span>Go back</span>
|
||||
</button>
|
||||
</div>
|
||||
<main id="main-content">
|
||||
<h1 transition:name={slugifyStr(title)} class="post-title">{title}</h1>
|
||||
<Datetime datetime={pubDatetime} size="lg" className="my-2" />
|
||||
<article id="article" role="article" class="prose mx-auto mt-8 max-w-3xl">
|
||||
<Content />
|
||||
</article>
|
||||
|
||||
<ul class="tags-container">
|
||||
{tags.map(tag => <Tag name={slugifyStr(tag)} />)}
|
||||
</ul>
|
||||
</main>
|
||||
<Footer />
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
main {
|
||||
@apply mx-auto w-full max-w-3xl px-4 pb-12;
|
||||
}
|
||||
.post-title {
|
||||
@apply text-2xl font-semibold text-skin-accent;
|
||||
}
|
||||
.tags-container {
|
||||
@apply my-8;
|
||||
}
|
||||
</style>
|
77
src/layouts/Posts.astro
Normal file
|
@ -0,0 +1,77 @@
|
|||
---
|
||||
import { SITE } from "@config";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Main from "@layouts/Main.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
import Footer from "@components/Footer.astro";
|
||||
import Card from "@components/Card";
|
||||
import LinkButton from "@components/LinkButton.astro";
|
||||
import slugify from "@utils/slugify";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
export interface Props {
|
||||
pageNum: number;
|
||||
totalPages: number;
|
||||
posts: CollectionEntry<"blog">[];
|
||||
}
|
||||
|
||||
const { pageNum, totalPages, posts } = Astro.props;
|
||||
|
||||
const prev = pageNum > 1 ? "" : "disabled";
|
||||
const next = pageNum < totalPages ? "" : "disabled";
|
||||
---
|
||||
|
||||
<Layout title={`Posts | ${SITE.title}`}>
|
||||
<Header activeNav="posts" />
|
||||
<Main pageTitle="Posts" pageDesc="All the articles I've posted.">
|
||||
<ul>
|
||||
{
|
||||
posts.map(({ data }) => (
|
||||
<Card href={`/posts/${slugify(data)}`} frontmatter={data} />
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</Main>
|
||||
|
||||
{
|
||||
totalPages > 1 && (
|
||||
<nav class="pagination-wrapper" aria-label="Pagination">
|
||||
<LinkButton
|
||||
disabled={prev === "disabled"}
|
||||
href={`/posts${pageNum - 1 !== 1 ? "/" + (pageNum - 1) : ""}`}
|
||||
className={`mr-4 select-none ${prev}`}
|
||||
ariaLabel="Previous"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class={`${prev}-svg`}>
|
||||
<path d="M12.707 17.293 8.414 13H18v-2H8.414l4.293-4.293-1.414-1.414L4.586 12l6.707 6.707z" />
|
||||
</svg>
|
||||
Prev
|
||||
</LinkButton>
|
||||
<LinkButton
|
||||
disabled={next === "disabled"}
|
||||
href={`/posts/${pageNum + 1}`}
|
||||
className={`ml-4 select-none ${next}`}
|
||||
ariaLabel="Next"
|
||||
>
|
||||
Next
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class={`${next}-svg`}>
|
||||
<path d="m11.293 17.293 1.414 1.414L19.414 12l-6.707-6.707-1.414 1.414L15.586 11H6v2h9.586z" />
|
||||
</svg>
|
||||
</LinkButton>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
<Footer noMarginTop={totalPages > 1} />
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
.pagination-wrapper {
|
||||
@apply mb-8 mt-auto flex justify-center;
|
||||
}
|
||||
.disabled {
|
||||
@apply pointer-events-none select-none opacity-50 hover:text-skin-base group-hover:fill-skin-base;
|
||||
}
|
||||
.disabled-svg {
|
||||
@apply group-hover:!fill-skin-base;
|
||||
}
|
||||
</style>
|
42
src/pages/404.astro
Normal file
|
@ -0,0 +1,42 @@
|
|||
---
|
||||
import { SITE } from "@config";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
import Footer from "@components/Footer.astro";
|
||||
import LinkButton from "@components/LinkButton.astro";
|
||||
---
|
||||
|
||||
<Layout title={`404 Not Found | ${SITE.title}`}>
|
||||
<Header />
|
||||
|
||||
<main id="main-content">
|
||||
<div class="not-found-wrapper">
|
||||
<h1 aria-label="404 Not Found">404</h1>
|
||||
<span aria-hidden="true">¯\_(ツ)_/¯</span>
|
||||
<p>Page Not Found</p>
|
||||
<LinkButton
|
||||
href="/"
|
||||
className="my-6 underline decoration-dashed underline-offset-8 text-lg"
|
||||
>
|
||||
Go back home
|
||||
</LinkButton>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
#main-content {
|
||||
@apply mx-auto flex max-w-3xl flex-1 items-center justify-center;
|
||||
}
|
||||
.not-found-wrapper {
|
||||
@apply mb-14 flex flex-col items-center justify-center;
|
||||
}
|
||||
.not-found-wrapper h1 {
|
||||
@apply text-9xl font-bold text-skin-accent;
|
||||
}
|
||||
.not-found-wrapper p {
|
||||
@apply mt-4 text-2xl sm:text-3xl;
|
||||
}
|
||||
</style>
|
6
src/pages/about.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
layout: ../layouts/AboutLayout.astro
|
||||
title: "About"
|
||||
---
|
||||
|
||||
Hi there I am Toastie a software developer from New Zealand I mostly write Discord bots and some other things, I hope you like what I post here.
|
152
src/pages/index.astro
Normal file
|
@ -0,0 +1,152 @@
|
|||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
import Footer from "@components/Footer.astro";
|
||||
import LinkButton from "@components/LinkButton.astro";
|
||||
import Hr from "@components/Hr.astro";
|
||||
import Card from "@components/Card";
|
||||
import Socials from "@components/Socials.astro";
|
||||
import getSortedPosts from "@utils/getSortedPosts";
|
||||
import slugify from "@utils/slugify";
|
||||
import { SOCIALS } from "@config";
|
||||
|
||||
const posts = await getCollection("blog");
|
||||
|
||||
const sortedPosts = getSortedPosts(posts);
|
||||
const featuredPosts = sortedPosts.filter(({ data }) => data.featured);
|
||||
|
||||
const socialCount = SOCIALS.filter(social => social.active).length;
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<Header />
|
||||
<main id="main-content">
|
||||
<section id="hero">
|
||||
<h1 class="mr-2">Welcome!
|
||||
</h1>
|
||||
<a
|
||||
target="_blank"
|
||||
href="/rss.xml"
|
||||
class="rss-link"
|
||||
aria-label="rss feed"
|
||||
title="RSS Feed"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="rss-icon"
|
||||
><path
|
||||
d="M19 20.001C19 11.729 12.271 5 4 5v2c7.168 0 13 5.832 13 13.001h2z"
|
||||
></path><path
|
||||
d="M12 20.001h2C14 14.486 9.514 10 4 10v2c4.411 0 8 3.589 8 8.001z"
|
||||
></path><circle cx="6" cy="18" r="2"></circle>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<p>
|
||||
Hi there, it looks like you have stumbled upon my blog,
|
||||
please go and get something to drink or something to eat
|
||||
while you read the contents of this blog.
|
||||
</p>
|
||||
{
|
||||
// only display if at least one social link is enabled
|
||||
socialCount > 0 && (
|
||||
<div class="social-wrapper">
|
||||
<div class="social-links">Social Links:</div>
|
||||
<Socials />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</section>
|
||||
|
||||
<Hr />
|
||||
|
||||
{
|
||||
featuredPosts.length > 0 && (
|
||||
<>
|
||||
<section id="featured">
|
||||
<h2>Featured</h2>
|
||||
<ul>
|
||||
{featuredPosts.map(({ data }) => (
|
||||
<Card
|
||||
href={`/posts/${slugify(data)}`}
|
||||
frontmatter={data}
|
||||
secHeading={false}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
<Hr />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
<section id="recent-posts">
|
||||
<h2>Recent Posts</h2>
|
||||
<ul>
|
||||
{
|
||||
sortedPosts
|
||||
.filter(({ data }) => !data.featured)
|
||||
.map(
|
||||
({ data }, index) =>
|
||||
index < 4 && (
|
||||
<Card
|
||||
href={`/posts/${slugify(data)}`}
|
||||
frontmatter={data}
|
||||
secHeading={false}
|
||||
/>
|
||||
)
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
<div class="all-posts-btn-wrapper">
|
||||
<LinkButton href="/posts">
|
||||
All Posts
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
><path
|
||||
d="m11.293 17.293 1.414 1.414L19.414 12l-6.707-6.707-1.414 1.414L15.586 11H6v2h9.586z"
|
||||
></path>
|
||||
</svg>
|
||||
</LinkButton>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
/* ===== Hero Section ===== */
|
||||
#hero {
|
||||
@apply pb-6 pt-8;
|
||||
}
|
||||
#hero h1 {
|
||||
@apply my-4 inline-block text-3xl font-bold sm:my-8 sm:text-5xl;
|
||||
}
|
||||
#hero .rss-link {
|
||||
@apply mb-6;
|
||||
}
|
||||
#hero .rss-icon {
|
||||
@apply mb-2 h-6 w-6 scale-110 fill-skin-accent sm:mb-3 sm:scale-125;
|
||||
}
|
||||
#hero p {
|
||||
@apply my-2;
|
||||
}
|
||||
.social-wrapper {
|
||||
@apply mt-4 flex flex-col sm:flex-row sm:items-center;
|
||||
}
|
||||
.social-links {
|
||||
@apply mb-1 mr-2 whitespace-nowrap sm:mb-0;
|
||||
}
|
||||
|
||||
/* ===== Featured & Recent Posts Sections ===== */
|
||||
#featured,
|
||||
#recent-posts {
|
||||
@apply pb-6 pt-12;
|
||||
}
|
||||
#featured h2,
|
||||
#recent-posts h2 {
|
||||
@apply text-2xl font-semibold tracking-wide;
|
||||
}
|
||||
.all-posts-btn-wrapper {
|
||||
@apply my-8 text-center;
|
||||
}
|
||||
</style>
|
|
@ -1,23 +0,0 @@
|
|||
/**
|
||||
* CSS files with the .module.css suffix will be treated as CSS modules
|
||||
* and scoped locally.
|
||||
*/
|
||||
|
||||
.heroBanner {
|
||||
padding: 4rem 0;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 996px) {
|
||||
.heroBanner {
|
||||
padding: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
---
|
||||
title: Markdown page example
|
||||
---
|
||||
|
||||
# Markdown page example
|
||||
|
||||
You don't need React to write simple standalone pages.
|
7
src/pages/og.png.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import type { APIRoute } from "astro";
|
||||
import { generateOgImageForSite } from "@utils/generateOgImages";
|
||||
|
||||
export const GET: APIRoute = async () =>
|
||||
new Response(await generateOgImageForSite(), {
|
||||
headers: { "Content-Type": "image/png" },
|
||||
});
|
58
src/pages/posts/[slug]/index.astro
Normal file
|
@ -0,0 +1,58 @@
|
|||
---
|
||||
import { type CollectionEntry, getCollection } from "astro:content";
|
||||
import Posts from "@layouts/Posts.astro";
|
||||
import PostDetails from "@layouts/PostDetails.astro";
|
||||
import getSortedPosts from "@utils/getSortedPosts";
|
||||
import getPageNumbers from "@utils/getPageNumbers";
|
||||
import slugify from "@utils/slugify";
|
||||
import { SITE } from "@config";
|
||||
|
||||
export interface Props {
|
||||
post: CollectionEntry<"blog">;
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getCollection("blog", ({ data }) => !data.draft);
|
||||
|
||||
const postResult = posts.map(post => ({
|
||||
params: { slug: slugify(post.data) },
|
||||
props: { post },
|
||||
}));
|
||||
|
||||
const pagePaths = getPageNumbers(posts.length).map(pageNum => ({
|
||||
params: { slug: String(pageNum) },
|
||||
}));
|
||||
|
||||
return [...postResult, ...pagePaths];
|
||||
}
|
||||
|
||||
const { slug } = Astro.params;
|
||||
const { post } = Astro.props;
|
||||
|
||||
const posts = await getCollection("blog");
|
||||
|
||||
const sortedPosts = getSortedPosts(posts);
|
||||
|
||||
const totalPages = getPageNumbers(sortedPosts.length);
|
||||
|
||||
const currentPage =
|
||||
slug && !isNaN(Number(slug)) && totalPages.includes(Number(slug))
|
||||
? Number(slug)
|
||||
: 0;
|
||||
const lastPost = currentPage * SITE.postPerPage;
|
||||
const startPost = lastPost - SITE.postPerPage;
|
||||
|
||||
const paginatedPosts = sortedPosts.slice(startPost, lastPost);
|
||||
---
|
||||
|
||||
{
|
||||
post ? (
|
||||
<PostDetails post={post} />
|
||||
) : (
|
||||
<Posts
|
||||
posts={paginatedPosts}
|
||||
pageNum={currentPage}
|
||||
totalPages={totalPages.length}
|
||||
/>
|
||||
)
|
||||
}
|
20
src/pages/posts/[slug]/index.png.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import type { APIRoute } from "astro";
|
||||
import { getCollection, type CollectionEntry } from "astro:content";
|
||||
import { generateOgImageForPost } from "@utils/generateOgImages";
|
||||
import { slugifyStr } from "@utils/slugify";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getCollection("blog").then(p =>
|
||||
p.filter(({ data }) => !data.draft && !data.ogImage)
|
||||
);
|
||||
|
||||
return posts.map(post => ({
|
||||
params: { slug: slugifyStr(post.data.title) },
|
||||
props: post,
|
||||
}));
|
||||
}
|
||||
|
||||
export const GET: APIRoute = async ({ props }) =>
|
||||
new Response(await generateOgImageForPost(props as CollectionEntry<"blog">), {
|
||||
headers: { "Content-Type": "image/png" },
|
||||
});
|
18
src/pages/posts/index.astro
Normal file
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
import { SITE } from "@config";
|
||||
import Posts from "@layouts/Posts.astro";
|
||||
import getSortedPosts from "@utils/getSortedPosts";
|
||||
import getPageNumbers from "@utils/getPageNumbers";
|
||||
|
||||
import { getCollection } from "astro:content";
|
||||
|
||||
const posts = await getCollection("blog");
|
||||
|
||||
const sortedPosts = getSortedPosts(posts);
|
||||
|
||||
const totalPages = getPageNumbers(sortedPosts.length);
|
||||
|
||||
const paginatedPosts = sortedPosts.slice(0, SITE.postPerPage);
|
||||
---
|
||||
|
||||
<Posts posts={paginatedPosts} pageNum={1} totalPages={totalPages.length} />
|
17
src/pages/robots.txt.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import type { APIRoute } from "astro";
|
||||
import { SITE } from "@config";
|
||||
|
||||
const robots = `
|
||||
User-agent: Googlebot
|
||||
Disallow: /nogooglebot/
|
||||
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: ${new URL("sitemap-index.xml", SITE.website).href}
|
||||
`.trim();
|
||||
|
||||
export const GET: APIRoute = () =>
|
||||
new Response(robots, {
|
||||
headers: { "Content-Type": "text/plain" },
|
||||
});
|
21
src/pages/rss.xml.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import rss from "@astrojs/rss";
|
||||
import { getCollection } from "astro:content";
|
||||
import getSortedPosts from "@utils/getSortedPosts";
|
||||
import slugify from "@utils/slugify";
|
||||
import { SITE } from "@config";
|
||||
|
||||
export async function GET() {
|
||||
const posts = await getCollection("blog");
|
||||
const sortedPosts = getSortedPosts(posts);
|
||||
return rss({
|
||||
title: SITE.title,
|
||||
description: SITE.desc,
|
||||
site: SITE.website,
|
||||
items: sortedPosts.map(({ data }) => ({
|
||||
link: `posts/${slugify(data)}`,
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
pubDate: new Date(data.pubDatetime),
|
||||
})),
|
||||
});
|
||||
}
|
27
src/pages/search.astro
Normal file
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import { SITE } from "@config";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Main from "@layouts/Main.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
import Footer from "@components/Footer.astro";
|
||||
import SearchBar from "@components/Search";
|
||||
|
||||
// Retrieve all articles
|
||||
const posts = await getCollection("blog", ({ data }) => !data.draft);
|
||||
|
||||
// List of items to search in
|
||||
const searchList = posts.map(({ data }) => ({
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
data,
|
||||
}));
|
||||
---
|
||||
|
||||
<Layout title={`Search | ${SITE.title}`}>
|
||||
<Header activeNav="search" />
|
||||
<Main pageTitle="Search" pageDesc="Search any article ...">
|
||||
<SearchBar client:load searchList={searchList} />
|
||||
</Main>
|
||||
<Footer />
|
||||
</Layout>
|
58
src/pages/tags/[tag].astro
Normal file
|
@ -0,0 +1,58 @@
|
|||
---
|
||||
import { type CollectionEntry, getCollection } from "astro:content";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Main from "@layouts/Main.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
import Footer from "@components/Footer.astro";
|
||||
import Card from "@components/Card";
|
||||
import getUniqueTags from "@utils/getUniqueTags";
|
||||
import getPostsByTag from "@utils/getPostsByTag";
|
||||
import slugify from "@utils/slugify";
|
||||
import { SITE } from "@config";
|
||||
import getSortedPosts from "@utils/getSortedPosts";
|
||||
|
||||
export interface Props {
|
||||
post: CollectionEntry<"blog">;
|
||||
tag: string;
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getCollection("blog");
|
||||
|
||||
const tags = getUniqueTags(posts);
|
||||
|
||||
return tags.map(tag => {
|
||||
return {
|
||||
params: { tag },
|
||||
props: { tag },
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const { tag } = Astro.props;
|
||||
|
||||
const posts = await getCollection("blog", ({ data }) => !data.draft);
|
||||
|
||||
const tagPosts = getPostsByTag(posts, tag);
|
||||
|
||||
const sortTagsPost = getSortedPosts(tagPosts);
|
||||
---
|
||||
|
||||
<Layout title={`Tag:${tag} | ${SITE.title}`}>
|
||||
<Header activeNav="tags" />
|
||||
<Main
|
||||
pageTitle={[`Tag:`, `${tag}`]}
|
||||
titleTransition={tag}
|
||||
pageDesc={`All the articles with the tag "${tag}".`}
|
||||
>
|
||||
<h1 slot="title" transition:name={tag}>{`Tag:${tag}`}</h1>
|
||||
<ul>
|
||||
{
|
||||
sortTagsPost.map(({ data }) => (
|
||||
<Card href={`/posts/${slugify(data)}`} frontmatter={data} />
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</Main>
|
||||
<Footer />
|
||||
</Layout>
|
24
src/pages/tags/index.astro
Normal file
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import Header from "@components/Header.astro";
|
||||
import Footer from "@components/Footer.astro";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Main from "@layouts/Main.astro";
|
||||
import Tag from "@components/Tag.astro";
|
||||
import getUniqueTags from "@utils/getUniqueTags";
|
||||
import { SITE } from "@config";
|
||||
|
||||
const posts = await getCollection("blog");
|
||||
|
||||
let tags = getUniqueTags(posts);
|
||||
---
|
||||
|
||||
<Layout title={`Tags | ${SITE.title}`}>
|
||||
<Header activeNav="tags" />
|
||||
<Main pageTitle="Tags" pageDesc="All the tags used in posts.">
|
||||
<ul>
|
||||
{tags.map(tag => <Tag name={tag} size="lg" />)}
|
||||
</ul>
|
||||
</Main>
|
||||
<Footer />
|
||||
</Layout>
|
135
src/styles/base.css
Normal file
|
@ -0,0 +1,135 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root,
|
||||
html[data-theme="light"] {
|
||||
--color-fill: 251, 254, 251;
|
||||
--color-text-base: 40, 39, 40;
|
||||
--color-accent: 0, 108, 172;
|
||||
--color-card: 230, 230, 230;
|
||||
--color-card-muted: 205, 205, 205;
|
||||
--color-border: 236, 233, 233;
|
||||
}
|
||||
html[data-theme="dark"] {
|
||||
--color-fill: 33, 39, 55;
|
||||
--color-text-base: 234, 237, 243;
|
||||
--color-accent: 255, 107, 1;
|
||||
--color-card: 52, 63, 96;
|
||||
--color-card-muted: 138, 51, 2;
|
||||
--color-border: 171, 75, 8;
|
||||
}
|
||||
#sun-svg,
|
||||
html[data-theme="dark"] #moon-svg {
|
||||
display: none;
|
||||
}
|
||||
#moon-svg,
|
||||
html[data-theme="dark"] #sun-svg {
|
||||
display: block;
|
||||
}
|
||||
body {
|
||||
@apply flex min-h-[100svh] flex-col bg-skin-fill font-mono text-skin-base
|
||||
selection:bg-skin-accent selection:bg-opacity-70 selection:text-skin-inverted;
|
||||
}
|
||||
section,
|
||||
footer {
|
||||
@apply mx-auto max-w-3xl px-4;
|
||||
}
|
||||
a {
|
||||
@apply outline-2 outline-offset-1 outline-skin-fill
|
||||
focus-visible:no-underline focus-visible:outline-dashed;
|
||||
}
|
||||
svg {
|
||||
@apply inline-block h-6 w-6 fill-skin-base group-hover:fill-skin-accent;
|
||||
}
|
||||
svg.icon-tabler {
|
||||
@apply inline-block h-6 w-6 scale-125 fill-transparent
|
||||
stroke-current stroke-2 opacity-90 group-hover:fill-transparent
|
||||
sm:scale-110;
|
||||
}
|
||||
.prose {
|
||||
@apply prose-headings:!mb-3 prose-headings:!text-skin-base
|
||||
prose-h3:italic prose-p:!text-skin-base
|
||||
prose-a:!text-skin-base prose-a:!decoration-dashed prose-a:underline-offset-8
|
||||
hover:prose-a:text-skin-accent prose-blockquote:!border-l-skin-accent
|
||||
prose-blockquote:border-opacity-50 prose-blockquote:opacity-80
|
||||
prose-figcaption:!text-skin-base prose-figcaption:opacity-70
|
||||
prose-strong:!text-skin-base
|
||||
|
||||
|
||||
prose-code:rounded prose-code:bg-skin-card
|
||||
prose-code:bg-opacity-75 prose-code:p-1 prose-code:!text-skin-base
|
||||
prose-code:before:!content-[''] prose-code:after:!content-['']
|
||||
prose-pre:!text-skin-base prose-ol:!text-skin-base
|
||||
prose-ul:overflow-x-clip prose-ul:!text-skin-base prose-li:marker:!text-skin-accent
|
||||
prose-table:text-skin-base prose-th:border
|
||||
prose-th:border-skin-line prose-td:border
|
||||
prose-td:border-skin-line prose-img:mx-auto
|
||||
prose-img:!mt-2 prose-img:border-2
|
||||
prose-img:border-skin-line prose-hr:!border-skin-line;
|
||||
}
|
||||
.prose a {
|
||||
@apply break-words hover:!text-skin-accent;
|
||||
}
|
||||
.prose thead th:first-child,
|
||||
tbody td:first-child,
|
||||
tfoot td:first-child {
|
||||
padding-left: 0.5714286em;
|
||||
}
|
||||
.prose h2#table-of-contents {
|
||||
@apply mb-2;
|
||||
}
|
||||
.prose details {
|
||||
@apply inline-block cursor-pointer select-none text-skin-base;
|
||||
}
|
||||
.prose summary {
|
||||
@apply focus-outline;
|
||||
}
|
||||
.prose h2#table-of-contents + p {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
/* ===== scrollbar ===== */
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
/* width */
|
||||
::-webkit-scrollbar {
|
||||
@apply w-3;
|
||||
}
|
||||
|
||||
/* Track */
|
||||
::-webkit-scrollbar-track {
|
||||
@apply bg-skin-fill;
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
::-webkit-scrollbar-thumb {
|
||||
@apply bg-skin-card;
|
||||
}
|
||||
|
||||
/* Handle on hover */
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
@apply bg-skin-card-muted;
|
||||
}
|
||||
|
||||
code,
|
||||
blockquote {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
pre > code {
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.display-none {
|
||||
@apply hidden;
|
||||
}
|
||||
.focus-outline {
|
||||
@apply outline-2 outline-offset-1 outline-skin-fill focus-visible:no-underline focus-visible:outline-dashed;
|
||||
}
|
||||
}
|
42
src/types.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
export type Site = {
|
||||
website: string;
|
||||
author: string;
|
||||
desc: string;
|
||||
title: string;
|
||||
ogImage?: string;
|
||||
lightAndDarkMode: boolean;
|
||||
postPerPage: number;
|
||||
};
|
||||
|
||||
export type SocialObjects = {
|
||||
name: SocialMedia;
|
||||
href: string;
|
||||
active: boolean;
|
||||
linkTitle: string;
|
||||
}[];
|
||||
|
||||
export type SocialIcons = {
|
||||
[social in SocialMedia]: string;
|
||||
};
|
||||
|
||||
export type SocialMedia =
|
||||
| "Github"
|
||||
| "Facebook"
|
||||
| "Instagram"
|
||||
| "LinkedIn"
|
||||
| "Mail"
|
||||
| "Twitter"
|
||||
| "Twitch"
|
||||
| "YouTube"
|
||||
| "WhatsApp"
|
||||
| "Snapchat"
|
||||
| "Pinterest"
|
||||
| "TikTok"
|
||||
| "CodePen"
|
||||
| "Discord"
|
||||
| "GitLab"
|
||||
| "Reddit"
|
||||
| "Skype"
|
||||
| "Steam"
|
||||
| "Telegram"
|
||||
| "Mastodon";
|
59
src/utils/generateOgImages.tsx
Normal file
|
@ -0,0 +1,59 @@
|
|||
import satori, { type SatoriOptions } from "satori";
|
||||
import { Resvg } from "@resvg/resvg-js";
|
||||
import { type CollectionEntry } from "astro:content";
|
||||
import postOgImage from "./og-templates/post";
|
||||
import siteOgImage from "./og-templates/site";
|
||||
|
||||
const fetchFonts = async () => {
|
||||
// Regular Font
|
||||
const fontFileRegular = await fetch(
|
||||
"https://www.1001fonts.com/download/font/ibm-plex-mono.regular.ttf"
|
||||
);
|
||||
const fontRegular: ArrayBuffer = await fontFileRegular.arrayBuffer();
|
||||
|
||||
// Bold Font
|
||||
const fontFileBold = await fetch(
|
||||
"https://www.1001fonts.com/download/font/ibm-plex-mono.bold.ttf"
|
||||
);
|
||||
const fontBold: ArrayBuffer = await fontFileBold.arrayBuffer();
|
||||
|
||||
return { fontRegular, fontBold };
|
||||
};
|
||||
|
||||
const { fontRegular, fontBold } = await fetchFonts();
|
||||
|
||||
const options: SatoriOptions = {
|
||||
width: 1200,
|
||||
height: 630,
|
||||
embedFont: true,
|
||||
fonts: [
|
||||
{
|
||||
name: "IBM Plex Mono",
|
||||
data: fontRegular,
|
||||
weight: 400,
|
||||
style: "normal",
|
||||
},
|
||||
{
|
||||
name: "IBM Plex Mono",
|
||||
data: fontBold,
|
||||
weight: 600,
|
||||
style: "normal",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
function svgBufferToPngBuffer(svg: string) {
|
||||
const resvg = new Resvg(svg);
|
||||
const pngData = resvg.render();
|
||||
return pngData.asPng();
|
||||
}
|
||||
|
||||
export async function generateOgImageForPost(post: CollectionEntry<"blog">) {
|
||||
const svg = await satori(postOgImage(post), options);
|
||||
return svgBufferToPngBuffer(svg);
|
||||
}
|
||||
|
||||
export async function generateOgImageForSite() {
|
||||
const svg = await satori(siteOgImage(), options);
|
||||
return svgBufferToPngBuffer(svg);
|
||||
}
|
14
src/utils/getPageNumbers.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { SITE } from "@config";
|
||||
|
||||
const getPageNumbers = (numberOfPosts: number) => {
|
||||
const numberOfPages = numberOfPosts / Number(SITE.postPerPage);
|
||||
|
||||
let pageNumbers: number[] = [];
|
||||
for (let i = 1; i <= Math.ceil(numberOfPages); i++) {
|
||||
pageNumbers = [...pageNumbers, i];
|
||||
}
|
||||
|
||||
return pageNumbers;
|
||||
};
|
||||
|
||||
export default getPageNumbers;
|
7
src/utils/getPostsByTag.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { slugifyAll } from "./slugify";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
const getPostsByTag = (posts: CollectionEntry<"blog">[], tag: string) =>
|
||||
posts.filter(post => slugifyAll(post.data.tags).includes(tag));
|
||||
|
||||
export default getPostsByTag;
|
12
src/utils/getSortedPosts.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
const getSortedPosts = (posts: CollectionEntry<"blog">[]) =>
|
||||
posts
|
||||
.filter(({ data }) => !data.draft)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
Math.floor(new Date(b.data.pubDatetime).getTime() / 1000) -
|
||||
Math.floor(new Date(a.data.pubDatetime).getTime() / 1000)
|
||||
);
|
||||
|
||||
export default getSortedPosts;
|
17
src/utils/getUniqueTags.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { slugifyStr } from "./slugify";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
const getUniqueTags = (posts: CollectionEntry<"blog">[]) => {
|
||||
const filteredPosts = posts.filter(({ data }) => !data.draft);
|
||||
const tags: string[] = filteredPosts
|
||||
.flatMap(post => post.data.tags)
|
||||
.map(tag => slugifyStr(tag))
|
||||
.filter(
|
||||
(value: string, index: number, self: string[]) =>
|
||||
self.indexOf(value) === index
|
||||
)
|
||||
.sort((tagA: string, tagB: string) => tagA.localeCompare(tagB));
|
||||
return tags;
|
||||
};
|
||||
|
||||
export default getUniqueTags;
|
96
src/utils/og-templates/post.tsx
Normal file
|
@ -0,0 +1,96 @@
|
|||
import { SITE } from "@config";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
export default (post: CollectionEntry<"blog">) => {
|
||||
return (
|
||||
<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>
|
||||
);
|
||||
};
|
87
src/utils/og-templates/site.tsx
Normal file
|
@ -0,0 +1,87 @@
|
|||
import { SITE } from "@config";
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<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>
|
||||
);
|
||||
};
|
11
src/utils/slugify.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { slug as slugger } from "github-slugger";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
export const slugifyStr = (str: string) => slugger(str);
|
||||
|
||||
const slugify = (post: CollectionEntry<"blog">["data"]) =>
|
||||
post.postSlug ? slugger(post.postSlug) : slugger(post.title);
|
||||
|
||||
export const slugifyAll = (arr: string[]) => arr.map(str => slugifyStr(str));
|
||||
|
||||
export default slugify;
|
Before Width: | Height: | Size: 219 KiB |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 219 KiB |
65
tailwind.config.cjs
Normal file
|
@ -0,0 +1,65 @@
|
|||
function withOpacity(variableName) {
|
||||
return ({ opacityValue }) => {
|
||||
if (opacityValue !== undefined) {
|
||||
return `rgba(var(${variableName}), ${opacityValue})`;
|
||||
}
|
||||
return `rgb(var(${variableName}))`;
|
||||
};
|
||||
}
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
|
||||
theme: {
|
||||
// Remove the following screen breakpoint or add other breakpoints
|
||||
// if one breakpoint is not enough for you
|
||||
screens: {
|
||||
sm: "640px",
|
||||
},
|
||||
|
||||
// Uncomment the following extend
|
||||
// if existing Tailwind color palette will be used
|
||||
|
||||
// extend: {
|
||||
textColor: {
|
||||
skin: {
|
||||
base: withOpacity("--color-text-base"),
|
||||
accent: withOpacity("--color-accent"),
|
||||
inverted: withOpacity("--color-fill"),
|
||||
},
|
||||
},
|
||||
backgroundColor: {
|
||||
skin: {
|
||||
fill: withOpacity("--color-fill"),
|
||||
accent: withOpacity("--color-accent"),
|
||||
inverted: withOpacity("--color-text-base"),
|
||||
card: withOpacity("--color-card"),
|
||||
"card-muted": withOpacity("--color-card-muted"),
|
||||
},
|
||||
},
|
||||
outlineColor: {
|
||||
skin: {
|
||||
fill: withOpacity("--color-accent"),
|
||||
},
|
||||
},
|
||||
borderColor: {
|
||||
skin: {
|
||||
line: withOpacity("--color-border"),
|
||||
fill: withOpacity("--color-text-base"),
|
||||
accent: withOpacity("--color-accent"),
|
||||
},
|
||||
},
|
||||
fill: {
|
||||
skin: {
|
||||
base: withOpacity("--color-text-base"),
|
||||
accent: withOpacity("--color-accent"),
|
||||
},
|
||||
transparent: "transparent",
|
||||
},
|
||||
fontFamily: {
|
||||
mono: ["IBM Plex Mono", "monospace"],
|
||||
},
|
||||
// },
|
||||
},
|
||||
plugins: [require("@tailwindcss/typography")],
|
||||
};
|
17
tsconfig.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src",
|
||||
"jsx": "react-jsx",
|
||||
"paths": {
|
||||
"@assets/*": ["assets/*"],
|
||||
"@config": ["config.ts"],
|
||||
"@components/*": ["components/*"],
|
||||
"@content/*": ["content/*"],
|
||||
"@layouts/*": ["layouts/*"],
|
||||
"@pages/*": ["pages/*"],
|
||||
"@styles/*": ["styles/*"],
|
||||
"@utils/*": ["utils/*"]
|
||||
}
|
||||
}
|
||||
}
|