Compare commits

..

104 commits

Author SHA1 Message Date
Ayo
1dbd3d662c 1.3.78 2026-05-31 18:48:18 +02:00
Ayo
199f3d1429 fix: links to legacy blog 2026-05-31 18:47:57 +02:00
Ayo
74a3a71090 1.3.77 2026-05-31 18:41:01 +02:00
Ayo
7966746711 feat(pgp): copy & download buttons 2026-05-31 18:40:50 +02:00
Ayo
7e0380a324 1.3.76 2026-05-31 17:04:22 +02:00
Ayo
7921721425 feat(pgp): concise description 2026-05-31 17:04:11 +02:00
Ayo
5fa6e68b37 1.3.75 2026-05-31 17:00:38 +02:00
Ayo
a71eb0f975 feat: add 'encryption' purpose for pgp key 2026-05-31 16:28:29 +02:00
Ayo
087403f70f 1.3.74 2026-05-31 13:11:35 +02:00
Ayo
25a83048d5 feat: remove chat link 2026-05-31 13:11:09 +02:00
Ayo
8f09af3829 1.3.73 2026-05-31 10:08:15 +02:00
Ayo
7ff435d619 feat(pgp): concise desc 2026-05-31 10:07:15 +02:00
Ayo
9d21f0282b 1.3.72 2026-05-31 08:42:01 +02:00
Ayo
5cc77e537b fix(pgp): meta descriptions 2026-05-31 08:41:42 +02:00
Ayo
05dd11db99 chore: patch, build, deploy script shortcut 2026-05-31 08:37:36 +02:00
Ayo
2e4cf74810 1.3.71 2026-05-31 08:34:50 +02:00
Ayo
5140c1fe55 feat(pgp): add title and description 2026-05-31 08:34:37 +02:00
Ayo
d9abb4bd53 1.3.70 2026-05-31 02:46:28 +02:00
Ayo
5104dc3176 feat: indicate pgp key expiry date 2026-05-31 02:44:33 +02:00
Ayo
5d2802aca7 feat: copy-able public key 2026-05-31 02:40:12 +02:00
Ayo
f0bb8f9272 feat: pgp key download link 2026-05-31 02:21:03 +02:00
Ayo
e4081c42e4 1.3.69 2026-05-30 23:04:13 +02:00
Ayo
9e4eb6ef15 feat: add PGP key 2026-05-30 23:03:59 +02:00
Ayo
a6f7c1153c feat: add gpg publickey 2026-05-30 22:05:13 +02:00
Ayo
cea29e7a01 chore: use eslint config helpers 2026-05-28 22:12:53 +02:00
Ayo
eaecdbcf23 chore: add eslint & prettier to workspace extensions 2026-05-28 22:09:52 +02:00
Ayo
9d69a6cc67 chore: add vitest-dom & sw test 2026-05-24 15:15:51 +02:00
Ayo
9b4312b49d test: prep-now 2026-05-23 23:26:32 +02:00
Ayo
bb335badf6 chore: add vitest 2026-05-23 23:25:44 +02:00
Ayo
85f9b148de chore: set allowBuild to false 2026-05-23 23:17:28 +02:00
Ayo
ca81b780ce chore: update pnpm & add allowBuild config 2026-05-23 23:16:48 +02:00
Ayo
0f28734a2d chore: add patch build deploy command to readme 2026-05-20 18:08:15 +02:00
Ayo
df5b89e5b1 chore: format 2026-05-17 11:20:17 +02:00
Ayo
8861d7f73d 1.3.68 2026-05-16 16:09:59 +02:00
Ayo
42112a8a9f feat: responsive now-wrapper 2026-05-16 16:09:18 +02:00
Ayo
08ef1cd749 1.3.67 2026-05-15 20:34:03 +02:00
Ayo
79c31a77d3 chore(deps): update astro-sw to v1 2026-05-15 20:33:18 +02:00
Ayo
4e6554e015 1.3.66 2026-05-15 19:03:30 +02:00
Ayo
88c159c33b fix: double define 2026-05-15 19:03:11 +02:00
Ayo
ec6e450205 1.3.65 2026-05-15 18:57:15 +02:00
Ayo
eaad49a9be feat: use import map for webcomponents; include to sw cache 2026-05-15 18:56:42 +02:00
Ayo
956c4b90e2 1.3.64 2026-05-15 16:07:40 +02:00
Ayo
1598336ed2 feat: show chat pill 2026-05-15 16:07:25 +02:00
Ayo
0072856eda 1.3.63 2026-05-15 15:16:04 +02:00
Ayo
cb8c26de0d chore: format code 2026-05-15 15:15:04 +02:00
Ayo
5b7497e508 feat: add chat.ayo.run to contact info 2026-05-15 15:14:14 +02:00
Ayo
8c89332c9d feat: add chat.ayo.run to contact info 2026-05-15 15:13:48 +02:00
Ayo
4c69f7d078 1.3.62 2026-05-13 21:12:00 +02:00
Ayo
11a8265221 feat: bring back real user monitoring for web perf 2026-05-13 21:11:47 +02:00
Ayo
952fcc9fed feat: update now content 2026-05-13 21:02:31 +02:00
Ayo
6adada53c4 1.3.61 2026-05-12 14:05:15 +02:00
Ayo
c772b78f3d feat: update now page 2026-05-12 14:05:06 +02:00
Ayo
c59557950c 1.3.60 2026-05-12 14:01:18 +02:00
Ayo
38b33194fd feat: update now page 2026-05-12 14:00:24 +02:00
Ayo
6f27d1b822 1.3.59 2026-05-09 08:02:42 +02:00
Ayo
97bb7c461d feat: update showcase page link to blog 2026-05-09 08:00:13 +02:00
Ayo
a4c398dcdc 1.3.58 2026-05-09 07:58:06 +02:00
Ayo
ef6d6bed62 feat: add <status-indicator> showcase card 2026-05-09 07:02:35 +02:00
Ayo
ab90625dd6 chore(deps): update to latest 2026-05-08 13:02:59 +02:00
ab65e3a957 1.3.57 2026-05-02 09:33:55 +02:00
86f9dd1dac feat: update about page 2026-05-02 09:33:23 +02:00
Ayo
b49903c481 chore: update package manager 2026-04-26 18:58:53 +02:00
Ayo
a6743064de fix: console.warn instead of warning() 2026-04-21 22:00:37 +02:00
Ayo
6fdd41fb0b chore(deps): update to latest 2026-04-21 21:12:06 +02:00
Ayo
c82244a2ad feat: add warning comment and console message to /threads placeholder page 2026-04-21 21:10:03 +02:00
Ayo
a3a3cdeca7 chore: test precommit on code.ayo.run 2026-04-21 19:54:29 +02:00
Ayo
198f45113a chore: update README.md to test code.ayo.run 2026-04-21 18:46:23 +02:00
Ayo
821701a82b chore: update README.md to test code.ayo.run dev environment 2026-04-21 18:45:07 +02:00
Ayo
7a9d1dbf92 chore: update README.md to test code.ayo.run dev environment 2026-04-21 18:44:35 +02:00
Ayo
263645d54c chore: update deps to latest; add lint:fix script 2026-04-17 13:22:19 +02:00
Ayo
7efb3e0ceb 1.3.56 2026-04-15 21:15:38 +02:00
Ayo
24a01f77c3 feat: update now 2026-04-15 21:15:26 +02:00
Ayo
c2b18490d4 feat: threads page explanation 2026-04-12 06:28:32 +02:00
Ayo
7938fd34c9 1.3.55 2026-04-11 14:53:13 +02:00
Ayo
0dab571b67 feat: new now page content 2026-04-11 14:52:37 +02:00
Ayo
65d37f8ede chore(deps): update to latest 2026-04-11 14:01:24 +02:00
Ayo
eb9039b5e9 feat: disable astro security config 2026-04-09 22:06:25 +02:00
Ayo
3284eccc6d chore(deps): update to latest 2026-03-31 23:20:35 +02:00
Ayo
9bb9ed4568 1.3.54 2026-03-27 14:16:42 +01:00
Ayo
28be4ac8c1 feat(cv): remove link to old github 2026-03-27 14:16:27 +01:00
Ayo
6ed6afe276 1.3.53 2026-03-27 07:55:21 +01:00
Ayo
c6a5f8d2c9 feat: update about contact section 2026-03-27 07:54:38 +01:00
Ayo
e2479de59f 1.3.52 2026-03-26 17:45:03 +01:00
Ayo
440c663751 chore: fix patch:build:deploy script 2026-03-26 17:44:04 +01:00
7f64bf9796 feat: link mcfly showcase card to repo @ git.ayo.run 2026-03-23 17:29:31 +01:00
e4d19324e0 chore: update package lock 2026-03-21 21:44:14 +01:00
Ayo
719324e471 feat: encourage reaching out 2026-03-21 18:04:54 +01:00
Ayo
6cea38e7f9 feat: update discord username 2026-03-21 18:02:17 +01:00
Ayo
18271d7ba5 feat: use ayco.io/gh for github 2026-03-19 21:24:08 +01:00
Ayo
8db452be42 feat: use ayco.io/gh for github 2026-03-19 17:21:14 +01:00
Ayo
ee5b9a1ef1 chore: remove verbose flag (-v) from scp script 2026-03-19 16:59:11 +01:00
Ayo
b4dc9500eb chore(deps): upgrade to latest; astro 6 upgrade 2026-03-18 21:06:21 +01:00
Ayo
3a16aee4b5 feat: archive astro-reactive-form showcase page 2026-03-18 21:05:44 +01:00
Ayo
ab8ce139ef feat: use hi@ayo.run as email 2026-03-18 11:27:11 +01:00
Ayo
7d0c78c029 chore: delete unused images 2026-03-14 23:00:45 +01:00
Ayo
35bb33cf8f chore: update scripts 2026-03-14 23:00:33 +01:00
Ayo
94cca820ad 1.3.51 2026-03-14 22:46:27 +01:00
Ayo
1867a6bbfd chore: push after deploy 2026-03-14 22:46:19 +01:00
Ayo
0f0fe2232c 1.3.50 2026-03-14 22:03:20 +01:00
Ayo
69a6543de7 feat: add codepen to links 2026-03-14 22:02:59 +01:00
Ayo
106e73fbcc 1.3.49 2026-03-14 20:26:49 +01:00
Ayo
5f6deea201 feat: remove sr.ht from links 2026-03-14 20:26:33 +01:00
Ayo
ed01a40995 1.3.48 2026-03-14 20:17:59 +01:00
Ayo
e7f64fd4a5 feat: set bsky profile to https://bsky.app/profile/ayo.run 2026-03-14 20:17:40 +01:00
36 changed files with 2112 additions and 3111 deletions

View file

@ -1,4 +1,8 @@
{
"recommendations": ["astro-build.astro-vscode"],
"recommendations": [
"astro-build.astro-vscode",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
],
"unwantedRecommendations": []
}

View file

@ -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.
---

View 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>

View file

@ -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',
},
],
}),
],
},
})

View file

@ -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'

View file

@ -1,7 +1,7 @@
{
"name": "@ayco/personal-website",
"type": "module",
"version": "1.3.47",
"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"
}
}

File diff suppressed because it is too large Load diff

4
pnpm-workspace.yaml Normal file
View 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
View 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-----

Binary file not shown.

Before

Width:  |  Height:  |  Size: 504 KiB

View file

@ -45,7 +45,7 @@ const year = new Date().getFullYear()
<p>
Copyright &#169; 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>

View file

@ -54,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>

View file

@ -0,0 +1,7 @@
{
"title": "BACK AT IT",
"description": "",
"publishDate": "2026-02-22",
"publishedOn": "the 22nd day of February 2026",
"publishState": ""
}

View 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. :)

View 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"
}

View 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!

View file

@ -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/',

View file

@ -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": ""
}

View file

@ -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!

View file

@ -127,10 +127,10 @@ const ogFileType = 'image/png'
}
</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>

View file

@ -1,3 +1,7 @@
.invisible {
display: none;
}
small {
font-size: var(--font-size-sm);
}

View file

@ -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> &middot
<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>

View file

@ -18,7 +18,7 @@ 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>,
@ -36,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>
@ -50,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">
@ -71,6 +68,7 @@ const avatarSize = 150
</ul>
</section>
</main>
<Footer />
</Layout>
@ -85,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 {
@ -192,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);
@ -223,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;
}

View file

@ -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>

View 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>

View 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
View 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>

View file

@ -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,7 +30,7 @@ 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"
/>

View file

@ -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
View 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
View 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)
})
})
})