Compare commits
118 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1dbd3d662c | |||
| 199f3d1429 | |||
| 74a3a71090 | |||
| 7966746711 | |||
| 7e0380a324 | |||
| 7921721425 | |||
| 5fa6e68b37 | |||
| a71eb0f975 | |||
| 087403f70f | |||
| 25a83048d5 | |||
| 8f09af3829 | |||
| 7ff435d619 | |||
| 9d21f0282b | |||
| 5cc77e537b | |||
| 05dd11db99 | |||
| 2e4cf74810 | |||
| 5140c1fe55 | |||
| d9abb4bd53 | |||
| 5104dc3176 | |||
| 5d2802aca7 | |||
| f0bb8f9272 | |||
| e4081c42e4 | |||
| 9e4eb6ef15 | |||
| a6f7c1153c | |||
| cea29e7a01 | |||
| eaecdbcf23 | |||
| 9d69a6cc67 | |||
| 9b4312b49d | |||
| bb335badf6 | |||
| 85f9b148de | |||
| ca81b780ce | |||
| 0f28734a2d | |||
| df5b89e5b1 | |||
| 8861d7f73d | |||
| 42112a8a9f | |||
| 08ef1cd749 | |||
| 79c31a77d3 | |||
| 4e6554e015 | |||
| 88c159c33b | |||
| ec6e450205 | |||
| eaad49a9be | |||
| 956c4b90e2 | |||
| 1598336ed2 | |||
| 0072856eda | |||
| cb8c26de0d | |||
| 5b7497e508 | |||
| 8c89332c9d | |||
| 4c69f7d078 | |||
| 11a8265221 | |||
| 952fcc9fed | |||
| 6adada53c4 | |||
| c772b78f3d | |||
| c59557950c | |||
| 38b33194fd | |||
| 6f27d1b822 | |||
| 97bb7c461d | |||
| a4c398dcdc | |||
| ef6d6bed62 | |||
| ab90625dd6 | |||
| ab65e3a957 | |||
| 86f9dd1dac | |||
| b49903c481 | |||
| a6743064de | |||
| 6fdd41fb0b | |||
| c82244a2ad | |||
| a3a3cdeca7 | |||
| 198f45113a | |||
| 821701a82b | |||
| 7a9d1dbf92 | |||
| 263645d54c | |||
| 7efb3e0ceb | |||
| 24a01f77c3 | |||
| c2b18490d4 | |||
| 7938fd34c9 | |||
| 0dab571b67 | |||
| 65d37f8ede | |||
| eb9039b5e9 | |||
| 3284eccc6d | |||
| 9bb9ed4568 | |||
| 28be4ac8c1 | |||
| 6ed6afe276 | |||
| c6a5f8d2c9 | |||
| e2479de59f | |||
| 440c663751 | |||
| 7f64bf9796 | |||
| e4d19324e0 | |||
| 719324e471 | |||
| 6cea38e7f9 | |||
| 18271d7ba5 | |||
| 8db452be42 | |||
| ee5b9a1ef1 | |||
| b4dc9500eb | |||
| 3a16aee4b5 | |||
| ab8ce139ef | |||
| 7d0c78c029 | |||
| 35bb33cf8f | |||
| 94cca820ad | |||
| 1867a6bbfd | |||
| 0f0fe2232c | |||
| 69a6543de7 | |||
| 106e73fbcc | |||
| 5f6deea201 | |||
| ed01a40995 | |||
| e7f64fd4a5 | |||
| 3d5ec86933 | |||
| da2ffe3bdb | |||
| 87451a1e58 | |||
| 65efc3b5ab | |||
| 637026f89a | |||
| be8025d416 | |||
| f986a9f5f0 | |||
| 4ca4dbc449 | |||
| 2f4a461b1d | |||
| 36f5dcbde0 | |||
| d2ffd9ed8a | |||
| 607a7720e5 | |||
| 634a1d446c | |||
| 48e3acde2f |
37 changed files with 2270 additions and 3147 deletions
6
.vscode/extensions.json
vendored
6
.vscode/extensions.json
vendored
|
|
@ -1,4 +1,8 @@
|
|||
{
|
||||
"recommendations": ["astro-build.astro-vscode"],
|
||||
"recommendations": [
|
||||
"astro-build.astro-vscode",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode"
|
||||
],
|
||||
"unwantedRecommendations": []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ $ pnpm i
|
|||
| `pnpm run build` | generate static files to `dist` directory |
|
||||
| `pnpm run deploy` | upload to my server |
|
||||
| `pnpm run prep:now` | back up and clear current `now page` constants |
|
||||
| `pnpm run patch:build:deploy` | increment version, build, then upload |
|
||||
|
||||
## Deployment
|
||||
|
||||
|
|
@ -46,7 +47,7 @@ If you want to run build before deploying, do `npm run build:deploy`
|
|||
|
||||
## Auto-mirror to various code forges
|
||||
|
||||
The power of git lies within its decentralized nature. I push this project to three different code forges, because I'm paranoid like that. No, I just like to show off. I do this with a post-commit hook that automatically calls `git push <repo> --mirror`, and you can see that in the `.husky/post-commit` file.
|
||||
I push this project to three different code forges, because I'm paranoid like that. No, I just like to show off. I do this with a post-commit hook that automatically calls `git push <repo> --mirror`, and you can see that in the `.husky/post-commit` file.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import GithubStats from 'astro-github-stats'
|
|||
<p>Embed GitHub stats on your Astro page</p>
|
||||
<ul>
|
||||
<li>
|
||||
GitHub repo: <a href="https://github.com/ayoayco/astro-github-stats"
|
||||
GitHub repo: <a href="https://ayco.io/gh/astro-github-stats"
|
||||
>astro-github-stats</a
|
||||
>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { defineConfig } from 'astro/config'
|
||||
import serviceWorker from '@ayco/astro-sw'
|
||||
import sitemap from '@astrojs/sitemap'
|
||||
import { viteStaticCopy } from 'vite-plugin-static-copy'
|
||||
|
||||
import * as data from './package.json'
|
||||
|
||||
|
|
@ -21,6 +22,10 @@ export default defineConfig({
|
|||
assetCachePrefix: 'ayco-personal-site',
|
||||
assetCacheVersionID: data.version,
|
||||
logAssets: true,
|
||||
include: [
|
||||
'/wc/node_modules/web-component-base/dist/index.js',
|
||||
'/wc/node_modules/@ayo-run/status-indicator/dist/status-indicator.js',
|
||||
],
|
||||
esbuild: {
|
||||
minify: true,
|
||||
},
|
||||
|
|
@ -38,4 +43,20 @@ export default defineConfig({
|
|||
},
|
||||
}),
|
||||
],
|
||||
vite: {
|
||||
plugins: [
|
||||
viteStaticCopy({
|
||||
targets: [
|
||||
{
|
||||
src: './node_modules/web-component-base/dist/index.js',
|
||||
dest: 'wc',
|
||||
},
|
||||
{
|
||||
src: './node_modules/@ayo-run/status-indicator/dist/status-indicator.js',
|
||||
dest: 'wc',
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import tseslint from 'typescript-eslint'
|
|||
import astroSwGlobals from '@ayco/astro-sw/globals'
|
||||
import astroParser from 'astro-eslint-parser'
|
||||
|
||||
import { includeIgnoreFile } from '@eslint/compat'
|
||||
import { includeIgnoreFile } from '@eslint/config-helpers'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
|
|
|
|||
57
package.json
57
package.json
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@ayco/personal-website",
|
||||
"type": "module",
|
||||
"version": "1.3.40",
|
||||
"version": "1.3.78",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"astro": "astro",
|
||||
|
|
@ -10,49 +10,54 @@
|
|||
"build": "astro telemetry disable && astro build",
|
||||
"preview": "astro preview",
|
||||
"lint": "eslint . --config eslint.config.mjs --cache",
|
||||
"lint:fix": "eslint . --config eslint.config.mjs --fix",
|
||||
"format": "prettier . --write",
|
||||
"check": "npm run format && npm run lint",
|
||||
"predeploy": "npm version patch && npm run build",
|
||||
"deploy": "npm run predeploy && eval $(grep '^HOST' .env) && scp -r dist ayo@$HOST:~/ayco.io-flask",
|
||||
"deploy": "eval $(grep '^HOST' .env) && scp -r dist ayo@$HOST:~/ayco.io-flask",
|
||||
"patch:build:deploy": "npm version patch && npm run build && npm run deploy",
|
||||
"pbd": "npm run patch:build:deploy",
|
||||
"build:preview": "npm run build && astro preview",
|
||||
"build:deploy": "npm run build && npm run deploy",
|
||||
"copy:dist": "npm run build && cp -R dist ../ayco.io-flask/",
|
||||
"prepare": "husky",
|
||||
"prep:now": "npx jiti ./commands/prep-now.js"
|
||||
"prep:now": "npx jiti ./commands/prep-now.js",
|
||||
"test": "vitest run ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astro-reactive/form": "^0.10.1",
|
||||
"@astro-reactive/validator": "^0.5.1",
|
||||
"@astrojs/sitemap": "^3.7.0",
|
||||
"@ayco/astro-sw": "^0.8.14",
|
||||
"@eslint/compat": "^2.0.2",
|
||||
"@astrojs/sitemap": "^3.7.2",
|
||||
"@ayco/astro-sw": "^1.0.0",
|
||||
"@eslint/compat": "^2.1.0",
|
||||
"@eslint/config-helpers": "^0.6.0",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@iconify-json/mdi": "^1.2.3",
|
||||
"@iconify-json/simple-icons": "^1.2.71",
|
||||
"@iconify-json/tabler": "^1.2.27",
|
||||
"@typescript-eslint/parser": "^8.56.0",
|
||||
"astro": "^5.17.3",
|
||||
"astro-eslint-parser": "^1.3.0",
|
||||
"@iconify-json/simple-icons": "^1.2.82",
|
||||
"@iconify-json/tabler": "^1.2.35",
|
||||
"@typescript-eslint/parser": "^8.59.3",
|
||||
"astro": "^6.3.3",
|
||||
"astro-eslint-parser": "^1.4.0",
|
||||
"astro-github-stats": "^0.8.0",
|
||||
"astro-icon": "^1.1.5",
|
||||
"consola": "^3.4.2",
|
||||
"eslint": "^10.0.1",
|
||||
"eslint-plugin-astro": "^1.6.0",
|
||||
"eslint": "^10.4.0",
|
||||
"eslint-plugin-astro": "^1.7.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||
"globals": "^17.3.0",
|
||||
"globals": "^17.6.0",
|
||||
"husky": "^9.1.7",
|
||||
"jiti": "^2.6.1",
|
||||
"lint-staged": "^16.2.7",
|
||||
"jiti": "^2.7.0",
|
||||
"lint-staged": "^17.0.4",
|
||||
"pathe": "^2.0.3",
|
||||
"prettier": "^3.8.1",
|
||||
"prettier": "^3.8.3",
|
||||
"prettier-plugin-astro": "^0.14.1",
|
||||
"rehype-stringify": "^10.0.1",
|
||||
"remark-parse": "^11.0.0",
|
||||
"remark-rehype": "^11.1.2",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.56.0",
|
||||
"unified": "^11.0.5"
|
||||
"typescript": "^6.0.3",
|
||||
"typescript-eslint": "^8.59.3",
|
||||
"unified": "^11.0.5",
|
||||
"vite-plugin-static-copy": "^4.1.0",
|
||||
"vitest": "^4.1.7",
|
||||
"vitest-dom": "^0.1.1"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,mjs,astro,ts}": [
|
||||
|
|
@ -63,5 +68,9 @@
|
|||
"prettier --write"
|
||||
]
|
||||
},
|
||||
"packageManager": "pnpm@10.14.0"
|
||||
"packageManager": "pnpm@11.2.2",
|
||||
"dependencies": {
|
||||
"@ayo-run/status-indicator": "^2.1.2",
|
||||
"web-component-base": "^4.1.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4391
pnpm-lock.yaml
4391
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
4
pnpm-workspace.yaml
Normal file
4
pnpm-workspace.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
allowBuilds:
|
||||
esbuild: false
|
||||
sharp: false
|
||||
web-component-base: false
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 305 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
13
public/publickey.asc
Normal file
13
public/publickey.asc
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mDMEahs0NhYJKwYBBAHaRw8BAQdAV2CfblniKxklPgW9eYt2gBl0jMHLwtjrQaY+
|
||||
BQoWdIa0FkF5byBBeWNvIDxheW9AYXljby5pbz6ImQQTFgoAQRYhBBfxPV6P9zcr
|
||||
E1RcOGXmv2QVKTxlBQJqGzQ2AhsDBQkB4TOABQsJCAcCAiICBhUKCQgLAgQWAgMB
|
||||
Ah4HAheAAAoJEGXmv2QVKTxlIeYA/2WLvkDapBbVmGXoACAhqcTN93/CoPyzUbhN
|
||||
hHE6mmAUAQDHsgCHmh3S/Rn4NRE3Gb41kiPTBWHrlYLTj3Kiw0ASB7g4BGobNDYS
|
||||
CisGAQQBl1UBBQEBB0C6OEKEIPLaNDOM6JYhvvq0Q4Mk/B1eyZBKF/b4fQvlGgMB
|
||||
CAeIfgQYFgoAJhYhBBfxPV6P9zcrE1RcOGXmv2QVKTxlBQJqGzQ2AhsMBQkB4TOA
|
||||
AAoJEGXmv2QVKTxlMSQBAP5ta1kUFp3HAYwcun8qmoiVq1dEJSN1LnI7HlX4ucTl
|
||||
AP0YLC768PFTBm9CM5T1BE0xjJ7s4dZSrVoI4n8RSe1nCA==
|
||||
=MTsv
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
|
|
@ -1,35 +1,137 @@
|
|||
Sitemap: https://ayo.ayco.io/sitemap-index.xml
|
||||
|
||||
User-agent: *
|
||||
Disallow:
|
||||
|
||||
User-agent: AdsBot-Google
|
||||
User-agent: AddSearchBot
|
||||
User-agent: AI2Bot
|
||||
User-agent: AI2Bot-DeepResearchEval
|
||||
User-agent: Ai2Bot-Dolma
|
||||
User-agent: aiHitBot
|
||||
User-agent: amazon-kendra
|
||||
User-agent: Amazonbot
|
||||
User-agent: AmazonBuyForMe
|
||||
User-agent: Amzn-SearchBot
|
||||
User-agent: Amzn-User
|
||||
User-agent: Andibot
|
||||
User-agent: Anomura
|
||||
User-agent: anthropic-ai
|
||||
User-agent: Applebot
|
||||
User-agent: AwarioRssBot
|
||||
User-agent: AwarioSmartBot
|
||||
User-agent: Applebot-Extended
|
||||
User-agent: atlassian-bot
|
||||
User-agent: Awario
|
||||
User-agent: AzureAI-SearchBot
|
||||
User-agent: bedrockbot
|
||||
User-agent: bigsur.ai
|
||||
User-agent: Bravebot
|
||||
User-agent: Brightbot 1.0
|
||||
User-agent: BuddyBot
|
||||
User-agent: Bytespider
|
||||
User-agent: CCBot
|
||||
User-agent: Channel3Bot
|
||||
User-agent: ChatGLM-Spider
|
||||
User-agent: ChatGPT Agent
|
||||
User-agent: ChatGPT-User
|
||||
User-agent: ClaudeBot
|
||||
User-agent: Claude-SearchBot
|
||||
User-agent: Claude-User
|
||||
User-agent: Claude-Web
|
||||
User-agent: ClaudeBot
|
||||
User-agent: Cloudflare-AutoRAG
|
||||
User-agent: CloudVertexBot
|
||||
User-agent: cohere-ai
|
||||
User-agent: DataForSeoBot
|
||||
User-agent: cohere-training-data-crawler
|
||||
User-agent: Cotoyogi
|
||||
User-agent: Crawl4AI
|
||||
User-agent: Crawlspace
|
||||
User-agent: Datenbank Crawler
|
||||
User-agent: DeepSeekBot
|
||||
User-agent: Devin
|
||||
User-agent: Diffbot
|
||||
User-agent: DuckAssistBot
|
||||
User-agent: Echobot Bot
|
||||
User-agent: EchoboxBot
|
||||
User-agent: FacebookBot
|
||||
User-agent: facebookexternalhit
|
||||
User-agent: Factset_spyderbot
|
||||
User-agent: FirecrawlAgent
|
||||
User-agent: FriendlyCrawler
|
||||
User-agent: Gemini-Deep-Research
|
||||
User-agent: Google-CloudVertexBot
|
||||
User-agent: Google-Extended
|
||||
User-agent: Google-Firebase
|
||||
User-agent: Google-NotebookLM
|
||||
User-agent: GoogleAgent-Mariner
|
||||
User-agent: GoogleOther
|
||||
User-agent: GoogleOther-Image
|
||||
User-agent: GoogleOther-Video
|
||||
User-agent: GPTBot
|
||||
User-agent: iAskBot
|
||||
User-agent: iaskspider
|
||||
User-agent: iaskspider/2.0
|
||||
User-agent: IbouBot
|
||||
User-agent: ICC-Crawler
|
||||
User-agent: ImagesiftBot
|
||||
User-agent: magpie-crawler
|
||||
User-agent: Meltwater
|
||||
User-agent: imageSpider
|
||||
User-agent: img2dataset
|
||||
User-agent: ISSCyberRiskCrawler
|
||||
User-agent: kagi-fetcher
|
||||
User-agent: Kangaroo Bot
|
||||
User-agent: KlaviyoAIBot
|
||||
User-agent: KunatoCrawler
|
||||
User-agent: laion-huggingface-processor
|
||||
User-agent: LAIONDownloader
|
||||
User-agent: LCC
|
||||
User-agent: LinerBot
|
||||
User-agent: Linguee Bot
|
||||
User-agent: LinkupBot
|
||||
User-agent: Manus-User
|
||||
User-agent: meta-externalagent
|
||||
User-agent: Meta-ExternalAgent
|
||||
User-agent: meta-externalfetcher
|
||||
User-agent: Meta-ExternalFetcher
|
||||
User-agent: meta-webindexer
|
||||
User-agent: MistralAI-User
|
||||
User-agent: MistralAI-User/1.0
|
||||
User-agent: MyCentralAIScraperBot
|
||||
User-agent: netEstate Imprint Crawler
|
||||
User-agent: NotebookLM
|
||||
User-agent: NovaAct
|
||||
User-agent: OAI-SearchBot
|
||||
User-agent: omgili
|
||||
User-agent: omgilibot
|
||||
User-agent: peer39_crawler
|
||||
User-agent: peer39_crawler/1.0
|
||||
User-agent: OpenAI
|
||||
User-agent: Operator
|
||||
User-agent: PanguBot
|
||||
User-agent: Panscient
|
||||
User-agent: panscient.com
|
||||
User-agent: Perplexity-User
|
||||
User-agent: PerplexityBot
|
||||
User-agent: PiplBot
|
||||
User-agent: Seekr
|
||||
User-agent: PetalBot
|
||||
User-agent: PhindBot
|
||||
User-agent: Poggio-Citations
|
||||
User-agent: Poseidon Research Crawler
|
||||
User-agent: QualifiedBot
|
||||
User-agent: QuillBot
|
||||
User-agent: quillbot.com
|
||||
User-agent: SBIntuitionsBot
|
||||
User-agent: Scrapy
|
||||
User-agent: SemrushBot-OCOB
|
||||
User-agent: SemrushBot-SWA
|
||||
User-agent: ShapBot
|
||||
User-agent: Sidetrade indexer bot
|
||||
User-agent: Spider
|
||||
User-agent: TavilyBot
|
||||
User-agent: TerraCotta
|
||||
User-agent: Thinkbot
|
||||
User-agent: TikTokSpider
|
||||
User-agent: Timpibot
|
||||
User-agent: TwinAgent
|
||||
User-agent: VelenPublicWebCrawler
|
||||
User-agent: WARDBot
|
||||
User-agent: Webzio-Extended
|
||||
User-agent: webzio-extended
|
||||
User-agent: wpbot
|
||||
User-agent: WRTNBot
|
||||
User-agent: YaK
|
||||
User-agent: YandexAdditional
|
||||
User-agent: YandexAdditionalBot
|
||||
User-agent: YouBot
|
||||
User-agent: ZanistaBot
|
||||
Disallow: /
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 504 KiB |
|
|
@ -45,7 +45,7 @@ const year = new Date().getFullYear()
|
|||
<p>
|
||||
Copyright © 2022-{year}
|
||||
<a href="/">Ayo Ayco</a>. This website <a
|
||||
href="https://ayos.blog/stopped-tracking-on-my-sites"
|
||||
href="https://v1.ayos.blog/stopped-tracking-on-my-sites"
|
||||
>does not track users</a
|
||||
>. See the <a href="https://git.ayo.run/ayo/ayco.io-astro">source code.</a>
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -3,17 +3,20 @@ export interface Props {
|
|||
title?: string | undefined
|
||||
description?: string | undefined
|
||||
ogImage?: string | undefined
|
||||
ogFileType?: string | undefined
|
||||
}
|
||||
const defaultDescription =
|
||||
'Professional software engineer specializing in web development with a decade of experience building web applications for both private businesses and government-funded high-impact projects utilizing web technologies, IoT, data viz/insights, remote sensing, and GIS'
|
||||
|
||||
const defaultTitle = 'Ayo Ayco - Tech Leader, Software Engineer, Web Developer'
|
||||
const defaultOgImage = 'ayo.png'
|
||||
const defaultOgFileType = 'image/png'
|
||||
|
||||
let {
|
||||
title,
|
||||
description = defaultDescription,
|
||||
ogImage = defaultOgImage,
|
||||
ogFileType = defaultOgFileType,
|
||||
} = Astro.props
|
||||
|
||||
const baseURL = Astro.site?.toString().slice(0, -1) // ?? 'https://ayo.ayco.io'
|
||||
|
|
@ -35,7 +38,8 @@ const baseURL = Astro.site?.toString().slice(0, -1) // ?? 'https://ayo.ayco.io'
|
|||
<meta property="og:url" content={baseURL + Astro.url.pathname} />
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:image" content={`/${ogImage}`} />
|
||||
<meta property="og:image:type" content={ogFileType} />
|
||||
<meta property="og:image" content={ogImage} />
|
||||
|
||||
<!-- Links -->
|
||||
<link rel="canonical" href={baseURL + Astro.url.pathname} />
|
||||
|
|
@ -50,4 +54,19 @@ const baseURL = Astro.site?.toString().slice(0, -1) // ?? 'https://ayo.ayco.io'
|
|||
<link rel="icon" href="favicon.svg" />
|
||||
<link rel="mask-icon" href="mask-icon.svg" color="#000000" />
|
||||
<link rel="apple-touch-icon" href="apple-touch-icon.png" />
|
||||
|
||||
<!-- Web Components -->
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"web-component-base": "/wc/node_modules/web-component-base/dist/index.js",
|
||||
"status-indicator": "/wc/node_modules/@ayo-run/status-indicator/dist/status-indicator.js"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script type="module">
|
||||
// importing is enough to register the elements
|
||||
// eslint-disable-next-line
|
||||
import StatusIndicator from 'status-indicator'
|
||||
</script>
|
||||
</head>
|
||||
|
|
|
|||
7
src/constants/bkup/2026-02-22.json
Normal file
7
src/constants/bkup/2026-02-22.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"title": "BACK AT IT",
|
||||
"description": "",
|
||||
"publishDate": "2026-02-22",
|
||||
"publishedOn": "the 22nd day of February 2026",
|
||||
"publishState": ""
|
||||
}
|
||||
9
src/constants/bkup/2026-02-22.md
Normal file
9
src/constants/bkup/2026-02-22.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
Hello! Been a while!
|
||||
|
||||
Yes, I'm getting back to some of my shelved side projects like <a href="https://ayco.io/gh/mcfly#readme">McFly</a> and <a href="https://ayco.io/gh/astro-sw#readme">Astro SW</a>.
|
||||
|
||||
Why were they shelved, you ask?
|
||||
|
||||
Well, that's a story for another time.
|
||||
|
||||
You can <a href="https://ayco.io/gh">Follow me on GitHub</a> to see my activities on these projects if that's your thing. :)
|
||||
7
src/constants/bkup/2026-04-11.json
Normal file
7
src/constants/bkup/2026-04-11.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"title": "Joining the Elk team, speaking my mind",
|
||||
"description": "",
|
||||
"publishDate": "2026-04-11",
|
||||
"publishedOn": "a sunny 11th day of April",
|
||||
"publishState": "in my home in Amsterdam"
|
||||
}
|
||||
23
src/constants/bkup/2026-04-11.md
Normal file
23
src/constants/bkup/2026-04-11.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
Aaaaah summer feels today. Bugs all buzzing and
|
||||
|
||||
All the darkness are fading and people who lived most of their lives in the tropics but emigrated to less sunny places become friendlier and nicer.
|
||||
|
||||
BUT NOT ME! 😡
|
||||
|
||||
kidding. 😅
|
||||
|
||||
Anyway... I have [joined the team](https://github.com/elk-zone/elk/pull/3594) behind [Elk](https://elk.zone) where I continue to be the bottle of sunshine that I am.
|
||||
|
||||
Kidding again.
|
||||
|
||||
...where I try to be more annoying than usual and tag @everyone in our Discord server. That's it. That's me now.
|
||||
|
||||
Also I have been smothering my [threads](https://ayo.ayco.io/threads) page with my deepest darkest thoughts -- and people either like it or not. That's how it is really. It's probably gonna be written on my grave: "HIS THOUGHTS SUCK"
|
||||
|
||||
...though honestly I have been getting more responses of validation and new connections that I have a feeling maybe I have been touching on something.... true???
|
||||
|
||||
Only time will tell. As my therapist said: these thoughts will not matter in 5 years.
|
||||
|
||||
...because they will be deleted.
|
||||
|
||||
Kidding!
|
||||
|
|
@ -51,25 +51,25 @@ export const footerLinks: Link[] = [
|
|||
icon: 'github',
|
||||
},
|
||||
{
|
||||
text: 'SourceHut',
|
||||
url: 'https://sr.ht/~ayoayco',
|
||||
icon: 'sourcehut',
|
||||
text: 'CodePen',
|
||||
url: 'https://codepen.io/ayo-run',
|
||||
icon: 'codepen',
|
||||
},
|
||||
{
|
||||
text: 'Mastodon',
|
||||
url: 'https://main.elk.zone/social.ayco.io/@ayo',
|
||||
icon: 'mastodon',
|
||||
},
|
||||
{
|
||||
text: 'Bluesky',
|
||||
url: 'http://bsky.app/profile/ayo.run',
|
||||
icon: 'bluesky',
|
||||
},
|
||||
{
|
||||
text: 'Pixelfed',
|
||||
url: 'https://metapixl.com/@ayo',
|
||||
icon: 'pixelfed',
|
||||
},
|
||||
{
|
||||
text: 'Bluesky',
|
||||
url: 'http://bsky.app/profile/ayo.ayco.io',
|
||||
icon: 'bluesky',
|
||||
},
|
||||
{
|
||||
text: 'Instagram',
|
||||
url: 'https://www.instagram.com/ayoayco/',
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"title": "BACK AT IT",
|
||||
"description": "",
|
||||
"publishDate": "2026-02-22",
|
||||
"publishedOn": "the 22nd day of February 2026",
|
||||
"title": "Social Web & Web Components",
|
||||
"description": "Getting into discussions about improving the social web & building components",
|
||||
"publishDate": "2026-05-12",
|
||||
"publishedOn": "",
|
||||
"publishState": ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
Hello! Been a while!
|
||||
I bought a pull-up bar, and in addition to a few barbells at home, I am starting to have a mini gym.
|
||||
|
||||
Yes, I'm getting back to some of my shelved side projects like <a href="https://github.com/ayoayco/mcfly#readme">McFly</a> and <a href="https://github.com/ayoayco/astro-sw#readme">Astro SW</a>.
|
||||
Super happy that I am getting healthier as I build my workout routines.
|
||||
|
||||
Why were they shelved, you ask?
|
||||
To keep my mind sharp, I find it helpful to have several hobby things I can get back to from time to time.
|
||||
|
||||
Well, that's a story for another time.
|
||||
I am in a few discussions for exploring some things in the open social web. Details can come later as they become more concrete.
|
||||
|
||||
You can <a href="https://github.com/ayoayco">Follow me on GitHub</a> to see my activities on these projects if that's your thing. :)
|
||||
Soft launch of my own newsletter is up on the canary version of my blog: https://main.ayos.blog/subscribe -- because email is the first decentralized social platform. Let's explore that idea more in the coming months.
|
||||
|
||||
I'm also back on my webcomponents main arc, after some side quests in self-hosting & machine learning.
|
||||
|
||||
Links to come later. Cheers!
|
||||
|
|
|
|||
|
|
@ -8,15 +8,34 @@ import { links } from '../constants/links'
|
|||
export interface Props {
|
||||
title?: string
|
||||
description?: string
|
||||
ogImage?: string
|
||||
}
|
||||
|
||||
const { title, description, ogImage } = Astro.props
|
||||
const { title, description } = Astro.props
|
||||
import { getImage } from 'astro:assets'
|
||||
|
||||
// fetch mastodon account
|
||||
const response = await fetch(
|
||||
'https://social.ayco.io/api/v1/accounts/lookup?acct=ayo'
|
||||
)
|
||||
const data = await response.json()
|
||||
const { avatar } = data
|
||||
const ogImage = await getImage({
|
||||
src: avatar,
|
||||
width: 400,
|
||||
height: 400,
|
||||
format: 'png',
|
||||
})
|
||||
const ogFileType = 'image/png'
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<Head title={title} description={description} ogImage={ogImage} />
|
||||
<Head
|
||||
title={title}
|
||||
description={description}
|
||||
ogImage={ogImage.src}
|
||||
ogFileType={ogFileType}
|
||||
/>
|
||||
|
||||
<body class="h-card">
|
||||
<Nav links={links} />
|
||||
|
|
@ -108,10 +127,10 @@ const { title, description, ogImage } = Astro.props
|
|||
}
|
||||
</style>
|
||||
<!-- Anonymous RUM for web perf by Cloudflare Web Analytics -->
|
||||
<script
|
||||
<!-- <script
|
||||
defer
|
||||
src="https://static.cloudflareinsights.com/beacon.min.js"
|
||||
data-cf-beacon='{"token": "a39ad600e67a4db8960c639d2552435c"}'></script>
|
||||
data-cf-beacon='{"token": "a39ad600e67a4db8960c639d2552435c"}'></script> -->
|
||||
<!-- End Cloudflare Web Analytics -->
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
.invisible {
|
||||
display: none;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,26 @@ import Footer from '../components/Footer.astro'
|
|||
target="_blank">iSAQB</a
|
||||
>.
|
||||
</p>
|
||||
<p>
|
||||
I have contributed software development expertise to
|
||||
<a href="http://infor.com">Infor</a>,
|
||||
<a href="https://www.priva.com/">Priva</a>,
|
||||
<a href="http://itc.uplb.edu.ph">UPLB</a>,
|
||||
<a href="http://dost.gov.ph">DOST</a>, and various government-funded
|
||||
projects such as
|
||||
<a href="http://noah.up.edu.ph">Project NOAH</a> — which consistently
|
||||
<a href="https://www.youtube.com/watch?v=LKrV6vtGZEA">saved lives</a>
|
||||
from natural hazards in
|
||||
<a href="https://www.officialgazette.gov.ph/programs/about-project-noah/">
|
||||
the Philippines
|
||||
</a>.
|
||||
</p>
|
||||
<p>
|
||||
In my spare time, I find it fun building <a href="/showcase">projects</a>,
|
||||
running self-hosted services at <a href="https://ayo.run">ayo.run</a>, and
|
||||
volunteering to <a href="https://ayos.blog/why-fediverse/" target="_blank"
|
||||
>Fediverse</a
|
||||
volunteering to <a
|
||||
href="https://v1.ayos.blog/why-fediverse/"
|
||||
target="_blank">Fediverse</a
|
||||
> projects like <a href="https://elk.zone">elk.zone</a>,
|
||||
<a href="https://m.webtoo.ls/public/local">webtoo.ls</a>, and <a
|
||||
href="https://m.webtoo.ls/@vitest">vitest's fedi presence</a
|
||||
|
|
@ -36,10 +51,11 @@ import Footer from '../components/Footer.astro'
|
|||
power of multi-generational & multi-cultural communities.
|
||||
</p>
|
||||
<h2 id="contact">Contact info</h2>
|
||||
<p>My inbox is open to everyone.</p>
|
||||
<ul>
|
||||
<li>✉️ Email: <a href="mailto:ayo@ayco.io">ayo@ayco.io</a></li>
|
||||
<li>
|
||||
👀 Fedi: <a href="https://social.ayco.io/@ayo">@ayo@ayco.io</a>
|
||||
✉️ Email me: <a href="mailto:ayo@ayco.io">ayo@ayco.io</a> ·
|
||||
<small> (<a href="/pgp">PGP key</a>)</small>
|
||||
</li>
|
||||
<li>
|
||||
💬 Signal: <a
|
||||
|
|
@ -47,19 +63,15 @@ import Footer from '../components/Footer.astro'
|
|||
>ayo.88</a
|
||||
>
|
||||
</li>
|
||||
<li>💬 Matrix: @ayoayco:matrix.org</li>
|
||||
<!--li>
|
||||
💬 Discord: <a href="https://discord.gg/kkvW7GYNAp">Ayo's Projects</a>
|
||||
</li-->
|
||||
<li>💬 Discord: ayoayco</li>
|
||||
<li>💬 Discord: ayo-run</li>
|
||||
</ul>
|
||||
</main>
|
||||
<Footer />
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
</style>
|
||||
</Layout>
|
||||
|
|
|
|||
|
|
@ -11,22 +11,18 @@ const response = await fetch(
|
|||
'https://social.ayco.io/api/v1/accounts/lookup?acct=ayo'
|
||||
)
|
||||
const data = await response.json()
|
||||
const { avatar } = data
|
||||
const { avatar, note } = data
|
||||
const avatarSize = 150
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<section class="highlighted-section">
|
||||
<div class="highlighted-section__content">
|
||||
<div style="display:none">
|
||||
<div class="invisible">
|
||||
<!-- h-card details -->
|
||||
<span class="p-name">Ayo Ayco</span>
|
||||
<a class="u-url u-uid" href="https://ayo.ayco.io">w</a>,
|
||||
<p class="p-note">
|
||||
Frontend guy who likes server adventures & works w/ linux. Looking to
|
||||
specialize in web perf, but mostly feels like an impostor for now. Has
|
||||
hobby projects.
|
||||
</p>
|
||||
<section class="p-note" set:html={note} />
|
||||
</div>
|
||||
<Picture
|
||||
class="u-photo highlighted-section__content__profile-picture"
|
||||
|
|
@ -40,8 +36,8 @@ const avatarSize = 150
|
|||
<h1 title="Ayo Ayco | Software Engineer + Web Developer">
|
||||
Hi, I'm <span class="heavy-text">Ayo</span>!
|
||||
</h1>
|
||||
<!--a href="https://forms.ayo.run/form/tnz7FybY" class="now-wrapper"-->
|
||||
<a href="/now" class="now-wrapper">
|
||||
|
||||
<a href="/now" class="now-wrapper action primary">
|
||||
<span class="now-label">now</span>
|
||||
<span class="status">{now.title}</span>
|
||||
</a>
|
||||
|
|
@ -54,9 +50,6 @@ const avatarSize = 150
|
|||
I care about the <em>Web</em>, and I love to <em>create</em> stuff to <em
|
||||
>inspire</em
|
||||
> and <em>serve</em> others.
|
||||
<!--
|
||||
<a href="/about">More?</a>
|
||||
-->
|
||||
</p>
|
||||
</section>
|
||||
<section class="cards-section">
|
||||
|
|
@ -75,6 +68,7 @@ const avatarSize = 150
|
|||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</Layout>
|
||||
|
||||
|
|
@ -89,14 +83,30 @@ const avatarSize = 150
|
|||
color: white;
|
||||
}
|
||||
|
||||
.now-wrapper {
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 40px;
|
||||
.action {
|
||||
margin-right: 0.5rem;
|
||||
padding: 8px 4px;
|
||||
font-weight: normal;
|
||||
color: white;
|
||||
font-size: var(--font-size-sm);
|
||||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
|
||||
&.primary {
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.now-wrapper {
|
||||
display: inline-block;
|
||||
max-width: 390px;
|
||||
white-space: nowrap;
|
||||
overflow: clip;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.status {
|
||||
max-width: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
.now-label {
|
||||
|
|
@ -136,8 +146,8 @@ const avatarSize = 150
|
|||
background-color: var(--color-brand-blue-1);
|
||||
display: block;
|
||||
border-radius: 50%;
|
||||
width: var(--avatarSize);
|
||||
height: var(--avatarSize);
|
||||
width: var(--avatarSize) px;
|
||||
height: var(--avatarSize) px;
|
||||
}
|
||||
|
||||
.highlighted-section__content ul a {
|
||||
|
|
@ -196,7 +206,7 @@ const avatarSize = 150
|
|||
}
|
||||
|
||||
@media only screen and (max-device-width: 360px) {
|
||||
.now-wrapper {
|
||||
.action.primary {
|
||||
border: 0px;
|
||||
border-radius: 0;
|
||||
font-size: var(--font-size-sm);
|
||||
|
|
@ -227,6 +237,12 @@ const avatarSize = 150
|
|||
}
|
||||
|
||||
@media only screen and (min-device-width: 280px) and (max-device-width: 653px) {
|
||||
.action {
|
||||
display: block;
|
||||
}
|
||||
.now-wrapper {
|
||||
max-width: 600px;
|
||||
}
|
||||
.highlighted-section__content {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,10 +42,9 @@ const description =
|
|||
</p>
|
||||
<p>
|
||||
In my spare time, I build side projects for people I love, like <a
|
||||
href="https://github.com/ayoayco/mnswpr#readme"
|
||||
href="https://ayco.io/gh/mnswpr#readme"
|
||||
target="_blank">mnswpr.com</a
|
||||
> and <a
|
||||
href="https://github.com/ayoayco/twists-and-shapes-and-turns#readme"
|
||||
> and <a href="https://ayco.io/gh/twists-and-shapes-and-turns#readme"
|
||||
>Kaboom!</a
|
||||
>
|
||||
</p>
|
||||
|
|
|
|||
70
src/pages/now/and-then/posts/2026-02-22.astro
Normal file
70
src/pages/now/and-then/posts/2026-02-22.astro
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
---
|
||||
import Layout from '../../../../layouts/Layout.astro'
|
||||
import Footer from '../../../../components/Footer.astro'
|
||||
|
||||
const title = `BACK AT IT`
|
||||
const description = ``
|
||||
let publishedOn = `the 22nd day of February 2026`
|
||||
const publishDate = `2026-02-22`
|
||||
const publishState = ``
|
||||
const content = `<p>Hello! Been a while!</p>
|
||||
<p>Yes, I'm getting back to some of my shelved side projects like McFly and Astro SW.</p>
|
||||
<p>Why were they shelved, you ask?</p>
|
||||
<p>Well, that's a story for another time.</p>
|
||||
<p>You can Follow me on GitHub to see my activities on these projects if that's your thing. :)</p>`
|
||||
|
||||
publishedOn = publishedOn === '' ? publishDate : publishedOn
|
||||
---
|
||||
|
||||
<Layout title={title} description={description}>
|
||||
<main>
|
||||
<h1><span class="text-gradient">{title}</span></h1>
|
||||
<p>
|
||||
<small>
|
||||
Published on
|
||||
<time datetime={publishDate}>
|
||||
{publishedOn}
|
||||
</time>
|
||||
{publishState}
|
||||
</small>
|
||||
</p>
|
||||
|
||||
<Fragment set:html={content} />
|
||||
</main>
|
||||
<Footer />
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
.text-gradient {
|
||||
font-weight: 900;
|
||||
background-image: var(--ayo-gradient);
|
||||
animation: pulse 4s ease-in-out infinite;
|
||||
background-size: 500% 500%;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-size: 100% 200%;
|
||||
background-position-y: 100%;
|
||||
border-radius: 0.4rem;
|
||||
}
|
||||
|
||||
.highlighted-content {
|
||||
margin: 1rem 0;
|
||||
background: #4f39fa;
|
||||
padding: 1rem;
|
||||
border-radius: 0.4rem;
|
||||
color: var(--color-bg);
|
||||
}
|
||||
|
||||
.highlighted-content code {
|
||||
font-size: var(--font-size-base);
|
||||
border: 0.1em solid var(--color-border);
|
||||
border-radius: 4px;
|
||||
padding: 0.15em 0.25em;
|
||||
}
|
||||
|
||||
.link-card-grid {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
77
src/pages/now/and-then/posts/2026-04-11.astro
Normal file
77
src/pages/now/and-then/posts/2026-04-11.astro
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
---
|
||||
import Layout from '../../../../layouts/Layout.astro'
|
||||
import Footer from '../../../../components/Footer.astro'
|
||||
|
||||
const title = `Joining the Elk team, speaking my mind`
|
||||
const description = ``
|
||||
let publishedOn = `a sunny 11th day of April`
|
||||
const publishDate = `2026-04-11`
|
||||
const publishState = `in my home in Amsterdam`
|
||||
const content = `<p>Aaaaah summer feels today. Bugs all buzzing and</p>
|
||||
<p>All the darkness are fading and people who lived most of their lives in the tropics but emigrated to less sunny places become friendlier and nicer.</p>
|
||||
<p>BUT NOT ME! 😡</p>
|
||||
<p>kidding. 😅</p>
|
||||
<p>Anyway... I have <a href="https://github.com/elk-zone/elk/pull/3594">joined the team</a> behind <a href="https://elk.zone">Elk</a> where I continue to be the bottle of sunshine that I am.</p>
|
||||
<p>Kidding again.</p>
|
||||
<p>...where I try to be more annoying than usual and tag @everyone in our Discord server. That's it. That's me now.</p>
|
||||
<p>Also I have been smothering my <a href="https://ayo.ayco.io/threads">threads</a> page with my deepest darkest thoughts -- and people either like it or not. That's how it is really. It's probably gonna be written on my grave: "HIS THOUGHTS SUCK"</p>
|
||||
<p>...though honestly I have been getting more responses of validation and new connections that I have a feeling maybe I have been touching on something.... true???</p>
|
||||
<p>Only time will tell. As my therapist said: these thoughts will not matter in 5 years.</p>
|
||||
<p>...because they will be deleted.</p>
|
||||
<p>Kidding!</p>`
|
||||
|
||||
publishedOn = publishedOn === '' ? publishDate : publishedOn
|
||||
---
|
||||
|
||||
<Layout title={title} description={description}>
|
||||
<main>
|
||||
<h1><span class="text-gradient">{title}</span></h1>
|
||||
<p>
|
||||
<small>
|
||||
Published on
|
||||
<time datetime={publishDate}>
|
||||
{publishedOn}
|
||||
</time>
|
||||
{publishState}
|
||||
</small>
|
||||
</p>
|
||||
|
||||
<Fragment set:html={content} />
|
||||
</main>
|
||||
<Footer />
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
.text-gradient {
|
||||
font-weight: 900;
|
||||
background-image: var(--ayo-gradient);
|
||||
animation: pulse 4s ease-in-out infinite;
|
||||
background-size: 500% 500%;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-size: 100% 200%;
|
||||
background-position-y: 100%;
|
||||
border-radius: 0.4rem;
|
||||
}
|
||||
|
||||
.highlighted-content {
|
||||
margin: 1rem 0;
|
||||
background: #4f39fa;
|
||||
padding: 1rem;
|
||||
border-radius: 0.4rem;
|
||||
color: var(--color-bg);
|
||||
}
|
||||
|
||||
.highlighted-content code {
|
||||
font-size: var(--font-size-base);
|
||||
border: 0.1em solid var(--color-border);
|
||||
border-radius: 4px;
|
||||
padding: 0.15em 0.25em;
|
||||
}
|
||||
|
||||
.link-card-grid {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
124
src/pages/pgp.astro
Normal file
124
src/pages/pgp.astro
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
---
|
||||
import Layout from '../layouts/Layout.astro'
|
||||
import Footer from '../components/Footer.astro'
|
||||
|
||||
const title = 'PGP public key'
|
||||
const description =
|
||||
'Use to verify my digital signature or to encrypt messages intended only for me.'
|
||||
---
|
||||
|
||||
<Layout title={"Ayo's " + title} description={description}>
|
||||
<main>
|
||||
<h1>My {title}</h1>
|
||||
<p>{description}</p>
|
||||
|
||||
<div class="key-block" role="region" aria-labelledby="public-key">
|
||||
<div class="btn-wrapper">
|
||||
<button class="copy-btn btn" aria-label="Copy key to clipboard"
|
||||
>Copy</button
|
||||
>
|
||||
<a href="/publickey.asc" class="btn">Download</a>
|
||||
</div>
|
||||
<pre
|
||||
id="public-key"><code>
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mDMEahs0NhYJKwYBBAHaRw8BAQdAV2CfblniKxklPgW9eYt2gBl0jMHLwtjrQaY+
|
||||
BQoWdIa0FkF5byBBeWNvIDxheW9AYXljby5pbz6ImQQTFgoAQRYhBBfxPV6P9zcr
|
||||
E1RcOGXmv2QVKTxlBQJqGzQ2AhsDBQkB4TOABQsJCAcCAiICBhUKCQgLAgQWAgMB
|
||||
Ah4HAheAAAoJEGXmv2QVKTxlIeYA/2WLvkDapBbVmGXoACAhqcTN93/CoPyzUbhN
|
||||
hHE6mmAUAQDHsgCHmh3S/Rn4NRE3Gb41kiPTBWHrlYLTj3Kiw0ASB7g4BGobNDYS
|
||||
CisGAQQBl1UBBQEBB0C6OEKEIPLaNDOM6JYhvvq0Q4Mk/B1eyZBKF/b4fQvlGgMB
|
||||
CAeIfgQYFgoAJhYhBBfxPV6P9zcrE1RcOGXmv2QVKTxlBQJqGzQ2AhsMBQkB4TOA
|
||||
AAoJEGXmv2QVKTxlMSQBAP5ta1kUFp3HAYwcun8qmoiVq1dEJSN1LnI7HlX4ucTl
|
||||
AP0YLC768PFTBm9CM5T1BE0xjJ7s4dZSrVoI4n8RSe1nCA==
|
||||
=MTsv
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
</code></pre>
|
||||
</div>
|
||||
<p>Expiry Date: 2027-05-30</p>
|
||||
<p>
|
||||
Fingerprint: <code
|
||||
>17F1 3D5E 8FF7 372B 1354 5C38 65E6 BF64 1529 3C65</code>
|
||||
</p>
|
||||
|
||||
<p></p>
|
||||
</main>
|
||||
<Footer />
|
||||
</Layout>
|
||||
|
||||
<script>
|
||||
/* Clipboard copy logic */
|
||||
document.querySelector('.copy-btn').addEventListener('click', async () => {
|
||||
const keyText = document.querySelector('#public-key code').innerText.trim()
|
||||
try {
|
||||
await navigator.clipboard.writeText(keyText)
|
||||
const btn = document.querySelector('.copy-btn')
|
||||
const original = btn.textContent
|
||||
btn.textContent = 'Copied!'
|
||||
setTimeout(() => {
|
||||
btn.textContent = original
|
||||
}, 2000)
|
||||
} catch (err) {
|
||||
console.error('Copy failed', err)
|
||||
alert('Unable to copy the key. Please copy it manually.')
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
code {
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
.key-block {
|
||||
position: relative;
|
||||
background: #272822;
|
||||
color: #f8f8f2;
|
||||
font-family: monospace;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.4;
|
||||
padding: 1rem 1rem 1rem 1.5rem;
|
||||
border-radius: 4px;
|
||||
overflow-x: auto;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.key-block code {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* buttons */
|
||||
.btn-wrapper {
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
background: var(--color-brand-blue-1);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 0.3rem 0.6rem;
|
||||
font-size: 0.8rem;
|
||||
cursor: pointer;
|
||||
opacity: 0.9;
|
||||
transition: opacity 0.2s;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn:hover,
|
||||
.btn:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.btn:focus {
|
||||
outline: 2px solid #0056b3;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -11,11 +11,17 @@ import Card from '../components/Card.astro'
|
|||
<main>
|
||||
<h1><span class="text-gradient">Hobby Projects</span></h1>
|
||||
<p>
|
||||
See more of my previous projects at <a href="https://ayos.blog/projects"
|
||||
>my blog</a
|
||||
See more of my previous projects at <a
|
||||
href="https://v1.ayos.blog/projects">my blog</a
|
||||
>.
|
||||
</p>
|
||||
<ul>
|
||||
<Card
|
||||
newTab
|
||||
href="https://status-indicator.webcomponent.io/"
|
||||
title="<status-indicator>"
|
||||
body="Colored circles that can pulse"
|
||||
/>
|
||||
<Card
|
||||
newTab
|
||||
href="https://git.ayo.run/ayo/simple-tts#readme"
|
||||
|
|
@ -24,13 +30,13 @@ import Card from '../components/Card.astro'
|
|||
/>
|
||||
<Card
|
||||
newTab
|
||||
href="https://mcfly.js.org"
|
||||
href="https://git.ayo.run/ayo/mcfly#readme"
|
||||
title="McFly"
|
||||
body="A meta-framework for building web experiences"
|
||||
/>
|
||||
<Card
|
||||
newTab
|
||||
href="https://ayco.io/sh/astro-sw#readme"
|
||||
href="https://git.ayo.run/ayo/astro-sw#readme"
|
||||
title="Astro SW"
|
||||
body="Integration to use your service worker with Astro"
|
||||
/>
|
||||
|
|
@ -42,15 +48,15 @@ import Card from '../components/Card.astro'
|
|||
/>
|
||||
<Card
|
||||
newTab
|
||||
href="https://ayco.io/sh/astro-resume#readme"
|
||||
href="https://git.ayo.run/ayo/astro-resume#readme"
|
||||
title="Astro Resume"
|
||||
body="Utilities for serializing data from server for use in the client with types preserved across components."
|
||||
/>
|
||||
<Card
|
||||
newTab
|
||||
href="https://ayco.io/n/generate-timezone-json"
|
||||
title="Time Zone JSON Generator"
|
||||
body="Generate a JSON file containing time zones from the official IANA Database or your own zone.tab file"
|
||||
href="https://astro-reactive.js.org"
|
||||
title="Astro Reactive"
|
||||
body="Let your Data build your UI with Astro components 🔥"
|
||||
/>
|
||||
<Card
|
||||
newTab
|
||||
|
|
@ -58,6 +64,12 @@ import Card from '../components/Card.astro'
|
|||
title="Cozy 🧸"
|
||||
body="Your modern-day reading assistant"
|
||||
/>
|
||||
<Card
|
||||
newTab
|
||||
href="https://ayco.io/n/generate-timezone-json"
|
||||
title="Time Zone JSON Generator"
|
||||
body="Generate a JSON file containing time zones from the official IANA Database or your own zone.tab file"
|
||||
/>
|
||||
<Card
|
||||
newTab
|
||||
href="https://kaboom.ayco.io"
|
||||
|
|
@ -70,11 +82,6 @@ import Card from '../components/Card.astro'
|
|||
title="Minesweeper"
|
||||
body="Recreated the classic game for the web 💣"
|
||||
/>
|
||||
<Card
|
||||
href="/showcase/astro-reactive-form"
|
||||
title="Reactive Form"
|
||||
body="The reactive form component for Astro 🔥"
|
||||
/>
|
||||
</ul>
|
||||
</main>
|
||||
<Footer />
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
---
|
||||
/**
|
||||
* The code for /threads in production is in: https://git.ayo.run/ayo/threads
|
||||
*/
|
||||
import Layout from '../layouts/Layout.astro'
|
||||
import Footer from '../components/Footer.astro'
|
||||
|
||||
|
|
@ -6,6 +9,10 @@ const page = {
|
|||
title: "Ayo's Threads",
|
||||
description: 'Incubator for thoughts before the become a blog',
|
||||
}
|
||||
|
||||
console.warn(
|
||||
'The code for /threads in production is in: https://git.ayo.run/ayo/threads'
|
||||
)
|
||||
---
|
||||
|
||||
<Layout title={page.title} description={page.description}>
|
||||
|
|
@ -14,6 +21,28 @@ const page = {
|
|||
|
||||
<p>{page.description}</p>
|
||||
<p>Visit the <a href="https://ayco.io/threads">page</a></p>
|
||||
<p>You're probably wondering how it works.</p>
|
||||
<p>
|
||||
What you are viewing now, is but a placeholder in the <a
|
||||
href="https://git.ayo.run/ayo/ayco.io-astro#readme"
|
||||
>static parts of my personal website.</a
|
||||
> This is not the full picture. There is a <a
|
||||
href="https://git.ayo.run/ayo/ayco.io-flask#readme">Flask server</a
|
||||
> serving the static assets who is also responsible for attaching dynamic parts
|
||||
/ blueprints like my <a href="https://git.ayo.run/ayo/threads#readme"
|
||||
>Threads</a
|
||||
> project.
|
||||
</p>
|
||||
<p>
|
||||
I built it that way, because Python serves as a good "glue" to put
|
||||
different parts together. Especially now with <em>LLMs</em>... where
|
||||
Python is probably their greatest strength.
|
||||
</p>
|
||||
<p>
|
||||
Anyway, for questions don't hesitate to <a
|
||||
href="https://ayo.ayco.io/about#contact">contact me</a
|
||||
>
|
||||
</p>
|
||||
</main>
|
||||
<Footer />
|
||||
</Layout>
|
||||
|
|
|
|||
121
tests/prep-now.test.ts
Normal file
121
tests/prep-now.test.ts
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
import { readFileSync, writeFileSync, copyFileSync } from 'node:fs'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { dirname, resolve } from 'node:path'
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { consola } from 'consola'
|
||||
|
||||
import newNow from '../commands/prep-now'
|
||||
|
||||
// Mock file system operations
|
||||
vi.mock('path', () => ({
|
||||
resolve: vi.fn(),
|
||||
readFileSync: vi.fn(),
|
||||
writeFileSync: vi.fn(),
|
||||
copyFileSync: vi.fn(),
|
||||
}))
|
||||
|
||||
// Mock consola
|
||||
vi.mock('consola', () => ({
|
||||
consola: {
|
||||
box: vi.fn(),
|
||||
start: vi.fn(),
|
||||
success: vi.fn(),
|
||||
fail: vi.fn(),
|
||||
error: vi.fn(),
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock mdToHTML
|
||||
vi.mock('../command/md-to-html', () => ({
|
||||
mdToHTML: vi.fn(),
|
||||
}))
|
||||
|
||||
describe('prep-now', () => {
|
||||
// const mockNow = {
|
||||
// title: 'Test Title',
|
||||
// description: 'Test Description',
|
||||
// publishedOn: '2023-01-01',
|
||||
// publishDate: '2023-01-01',
|
||||
// publishState: 'draft',
|
||||
// content: '# Test Content',
|
||||
// }
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
vi.mocked(resolve).mockImplementation((...paths) => paths.join('/'))
|
||||
vi.mocked(readFileSync).mockReturnValue('template content')
|
||||
vi.mocked(writeFileSync).mockImplementation(() => {})
|
||||
vi.mocked(copyFileSync).mockImplementation(() => {})
|
||||
vi.mocked(consola.box).mockImplementation(() => {})
|
||||
vi.mocked(consola.start).mockImplementation(() => {})
|
||||
vi.mocked(consola.success).mockImplementation(() => {})
|
||||
vi.mocked(consola.fail).mockImplementation(() => {})
|
||||
vi.mocked(consola.error).mockImplementation(() => {})
|
||||
})
|
||||
|
||||
it('should create a new now page and clear the original files', async () => {
|
||||
// Mock the mdToHTML function
|
||||
const { mdToHTML } = await import('../utils/md-to-html')
|
||||
vi.mocked(mdToHTML).mockResolvedValue('<h1>Test Content</h1>')
|
||||
|
||||
// Mock file paths
|
||||
vi.mocked(resolve)
|
||||
.mockReturnValueOnce('/src/constants/now.md')
|
||||
.mockReturnValueOnce('/src/constants/now.json')
|
||||
.mockReturnValueOnce('/src/pages/now/and-then/posts/2023-01-01.astro')
|
||||
.mockReturnValueOnce('/src/constants/bkup/2023-01-01.md')
|
||||
.mockReturnValueOnce('/src/constants/bkup/2023-01-01.json')
|
||||
|
||||
// Mock file content
|
||||
vi.mocked(readFileSync)
|
||||
.mockReturnValueOnce('template content')
|
||||
.mockReturnValueOnce('# Test Content')
|
||||
|
||||
// Mock file paths
|
||||
const __filename = '/src/commands/prep-now.ts'
|
||||
const __dirname = '/src/commands'
|
||||
vi.mocked(fileURLToPath).mockReturnValue(__filename)
|
||||
vi.mocked(dirname).mockReturnValue(__dirname)
|
||||
|
||||
// Execute the function
|
||||
await newNow()
|
||||
|
||||
// Verify file operations
|
||||
expect(writeFileSync).toHaveBeenCalledTimes(2)
|
||||
expect(copyFileSync).toHaveBeenCalledTimes(2)
|
||||
expect(consola.success).toHaveBeenCalledWith('now.md cleared')
|
||||
expect(consola.success).toHaveBeenCalledWith(
|
||||
'You may now update your Now content and props.\n'
|
||||
)
|
||||
})
|
||||
|
||||
it('should handle errors gracefully', async () => {
|
||||
// Mock an error
|
||||
vi.mocked(writeFileSync).mockImplementation(() => {
|
||||
throw new Error('Write failed')
|
||||
})
|
||||
|
||||
// Mock file paths
|
||||
vi.mocked(resolve)
|
||||
.mockReturnValueOnce('/src/constants/now.md')
|
||||
.mockReturnValueOnce('/src/constants/now.json')
|
||||
.mockReturnValueOnce('/src/pages/now/and-then/posts/2023-01-01.astro')
|
||||
.mockReturnValueOnce('/src/constants/bkup/2023-01-01.md')
|
||||
.mockReturnValueOnce('/src/constants/bkup/2023-01-01.json')
|
||||
|
||||
// Mock file content
|
||||
vi.mocked(readFileSync)
|
||||
.mockReturnValueOnce('template content')
|
||||
.mockReturnValueOnce('# Test Content')
|
||||
|
||||
// Execute the function
|
||||
await newNow()
|
||||
|
||||
// Verify error handling
|
||||
expect(consola.fail).toHaveBeenCalledWith(
|
||||
'Failed to create a new post from Now'
|
||||
)
|
||||
expect(consola.error).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
91
tests/sw.test.js
Normal file
91
tests/sw.test.js
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { install } from 'vitest-dom'
|
||||
|
||||
// Install DOM environment for vitest
|
||||
install()
|
||||
|
||||
// Mock the service worker globals
|
||||
global.caches = {
|
||||
keys: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
open: vi.fn(),
|
||||
match: vi.fn(),
|
||||
}
|
||||
|
||||
// Mock the global self object
|
||||
global.self = {
|
||||
skipWaiting: vi.fn(),
|
||||
addEventListener: vi.fn(),
|
||||
}
|
||||
|
||||
// Mock the fetch function
|
||||
global.fetch = vi.fn()
|
||||
|
||||
// Import the service worker module
|
||||
// Note: We need to use dynamic import to avoid module loading issues
|
||||
let swModule
|
||||
|
||||
beforeEach(async () => {
|
||||
// Clear all mocks
|
||||
vi.clearAllMocks()
|
||||
|
||||
// Mock the module
|
||||
swModule = await import('../src/sw.mjs')
|
||||
})
|
||||
|
||||
describe('Service Worker', () => {
|
||||
describe('cleanOldCaches', () => {
|
||||
it('should delete old caches', async () => {
|
||||
const cacheName = 'app-v000'
|
||||
const oldCacheName = 'app-v001'
|
||||
|
||||
global.caches.keys.mockResolvedValue([cacheName, oldCacheName])
|
||||
global.caches.delete.mockResolvedValue(true)
|
||||
|
||||
await swModule.cleanOldCaches()
|
||||
|
||||
expect(global.caches.delete).toHaveBeenCalledWith(oldCacheName)
|
||||
})
|
||||
})
|
||||
|
||||
describe('addResourcesToCache', () => {
|
||||
it('should add resources to cache', async () => {
|
||||
const resources = ['/index.html', '/style.css']
|
||||
const cache = { addAll: vi.fn() }
|
||||
|
||||
global.caches.open.mockResolvedValue(cache)
|
||||
|
||||
await swModule.addResourcesToCache(resources)
|
||||
|
||||
expect(cache.addAll).toHaveBeenCalledWith(resources)
|
||||
})
|
||||
})
|
||||
|
||||
describe('networkFirst', () => {
|
||||
it('should return network response when available', async () => {
|
||||
const request = new Request('/test')
|
||||
const networkResponse = new Response('network content')
|
||||
|
||||
global.fetch.mockResolvedValue(networkResponse)
|
||||
global.caches.open.mockResolvedValue({ put: vi.fn() })
|
||||
|
||||
const result = await swModule.networkFirst({ request })
|
||||
|
||||
expect(result).toEqual(networkResponse)
|
||||
})
|
||||
|
||||
it('should return cached response when network fails', async () => {
|
||||
const request = new Request('/test')
|
||||
const cachedResponse = new Response('cached content')
|
||||
|
||||
global.fetch.mockRejectedValue(new Error('Network error'))
|
||||
global.caches.open.mockResolvedValue({
|
||||
match: vi.fn().mockResolvedValue(cachedResponse),
|
||||
})
|
||||
|
||||
const result = await swModule.networkFirst({ request })
|
||||
|
||||
expect(result).toEqual(cachedResponse)
|
||||
})
|
||||
})
|
||||
})
|
||||
Loading…
Reference in a new issue