Compare commits
32 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d8a38b60e3 | |||
| 1ae9325ace | |||
| 73ecdbe4d8 | |||
| bd65162c70 | |||
| fb93eb7574 | |||
| ed4b091cf1 | |||
| 778328d984 | |||
| 44e6fe6722 | |||
| 271a92c7b3 | |||
| d79e51f1ce | |||
| cdc9d091e3 | |||
| ef5c186345 | |||
| 8c2e355bfb | |||
| a9dc107176 | |||
| ec7442da30 | |||
| 7a674694a7 | |||
| d8768998e2 | |||
| 2f39f3d62d | |||
| 28a57aca9a | |||
| e77351e5dd | |||
| fd1d015a18 | |||
| f2bbfbf1b4 | |||
| 4860085aef | |||
| e3be0e92cb | |||
| 1dbd3d662c | |||
| 199f3d1429 | |||
| 74a3a71090 | |||
| 7966746711 | |||
| 7e0380a324 | |||
| 7921721425 | |||
| 5fa6e68b37 | |||
| a71eb0f975 |
10 changed files with 127 additions and 51 deletions
31
README.md
31
README.md
|
|
@ -22,13 +22,30 @@ $ pnpm i
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
| Command | Action |
|
| Command | Action |
|
||||||
| ------------------- | ---------------------------------------------- |
|
| ----------------------------- | ---------------------------------------------- |
|
||||||
| `pnpm run dev` | start dev server |
|
| `pnpm run dev` | start dev server |
|
||||||
| `pnpm run build` | generate static files to `dist` directory |
|
| `pnpm run build` | generate static files to `dist` directory |
|
||||||
| `pnpm run deploy` | upload to my server |
|
| `pnpm run deploy` | upload to my server |
|
||||||
| `pnpm run prep:now` | back up and clear current `now page` constants |
|
| `pnpm run prep:now` | back up and clear current `now page` constants |
|
||||||
| `pnpm run patch:build:deploy` | increment version, build, then upload |
|
| `pnpm run patch:build:deploy` | increment version, build, then upload |
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
This project uses Vitest for unit tests. The test script is defined in `package.json` as `vitest run .`.
|
||||||
|
|
||||||
|
Run tests locally with pnpm (recommended):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# install dependencies (if you haven't already)
|
||||||
|
pnpm i
|
||||||
|
|
||||||
|
# run tests once
|
||||||
|
pnpm test
|
||||||
|
|
||||||
|
# run Vitest in interactive/watch mode
|
||||||
|
pnpm exec vitest
|
||||||
|
```
|
||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
|
|
|
||||||
16
package.json
16
package.json
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@ayco/personal-website",
|
"name": "@ayco/personal-website",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "1.3.74",
|
"version": "1.3.88",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"astro": "astro",
|
"astro": "astro",
|
||||||
|
|
@ -11,8 +11,9 @@
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
"lint": "eslint . --config eslint.config.mjs --cache",
|
"lint": "eslint . --config eslint.config.mjs --cache",
|
||||||
"lint:fix": "eslint . --config eslint.config.mjs --fix",
|
"lint:fix": "eslint . --config eslint.config.mjs --fix",
|
||||||
"format": "prettier . --write",
|
"fmt": "prettier . --config prettier.config.mjs --check",
|
||||||
"check": "npm run format && npm run lint",
|
"fmt:fix": "prettier . --config prettier.config.mjs --write",
|
||||||
|
"check": "npm run fmt && npm run lint",
|
||||||
"deploy": "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",
|
"patch:build:deploy": "npm version patch && npm run build && npm run deploy",
|
||||||
"pbd": "npm run patch:build:deploy",
|
"pbd": "npm run patch:build:deploy",
|
||||||
|
|
@ -51,6 +52,7 @@
|
||||||
"rehype-stringify": "^10.0.1",
|
"rehype-stringify": "^10.0.1",
|
||||||
"remark-parse": "^11.0.0",
|
"remark-parse": "^11.0.0",
|
||||||
"remark-rehype": "^11.1.2",
|
"remark-rehype": "^11.1.2",
|
||||||
|
"sharp": "^0.34.5",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^6.0.3",
|
"typescript": "^6.0.3",
|
||||||
"typescript-eslint": "^8.59.3",
|
"typescript-eslint": "^8.59.3",
|
||||||
|
|
@ -61,14 +63,14 @@
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js,mjs,astro,ts}": [
|
"*.{js,mjs,astro,ts}": [
|
||||||
"prettier --write",
|
"prettier . --config prettier.config.mjs --write",
|
||||||
"eslint --fix"
|
"eslint . --config eslint.config.mjs --fix"
|
||||||
],
|
],
|
||||||
"*.json": [
|
"*.json": [
|
||||||
"prettier --write"
|
"prettier . --config prettier.config.mjs --write"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@11.2.2",
|
"packageManager": "pnpm@11.5.2+sha512.71c631e382066efc25625d5cf029075de07b61b37f6e27350fbd84b1bda5864c8c1967adc280776b45c30a715c0359a3be08fef42d5bb09e2b99029979692916",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ayo-run/status-indicator": "^2.1.2",
|
"@ayo-run/status-indicator": "^2.1.2",
|
||||||
"web-component-base": "^4.1.2"
|
"web-component-base": "^4.1.2"
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,9 @@ importers:
|
||||||
remark-rehype:
|
remark-rehype:
|
||||||
specifier: ^11.1.2
|
specifier: ^11.1.2
|
||||||
version: 11.1.2
|
version: 11.1.2
|
||||||
|
sharp:
|
||||||
|
specifier: ^0.34.5
|
||||||
|
version: 0.34.5
|
||||||
tslib:
|
tslib:
|
||||||
specifier: ^2.8.1
|
specifier: ^2.8.1
|
||||||
version: 2.8.1
|
version: 2.8.1
|
||||||
|
|
@ -3255,8 +3258,7 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@img/colour@1.1.0':
|
'@img/colour@1.1.0': {}
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@img/sharp-darwin-arm64@0.34.5':
|
'@img/sharp-darwin-arm64@0.34.5':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
|
|
@ -4122,8 +4124,7 @@ snapshots:
|
||||||
|
|
||||||
destr@2.0.5: {}
|
destr@2.0.5: {}
|
||||||
|
|
||||||
detect-libc@2.1.2:
|
detect-libc@2.1.2: {}
|
||||||
optional: true
|
|
||||||
|
|
||||||
devalue@5.8.1: {}
|
devalue@5.8.1: {}
|
||||||
|
|
||||||
|
|
@ -5775,7 +5776,6 @@ snapshots:
|
||||||
'@img/sharp-win32-arm64': 0.34.5
|
'@img/sharp-win32-arm64': 0.34.5
|
||||||
'@img/sharp-win32-ia32': 0.34.5
|
'@img/sharp-win32-ia32': 0.34.5
|
||||||
'@img/sharp-win32-x64': 0.34.5
|
'@img/sharp-win32-x64': 0.34.5
|
||||||
optional: true
|
|
||||||
|
|
||||||
shebang-command@2.0.0:
|
shebang-command@2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ export const footerLinks: Link[] = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Mastodon',
|
text: 'Mastodon',
|
||||||
url: 'https://main.elk.zone/social.ayco.io/@ayo',
|
url: 'https://elk.zone/m.webtoo.ls/@ayo',
|
||||||
icon: 'mastodon',
|
icon: 'mastodon',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import { getImage } from 'astro:assets'
|
||||||
|
|
||||||
// fetch mastodon account
|
// fetch mastodon account
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
'https://social.ayco.io/api/v1/accounts/lookup?acct=ayo'
|
'https://m.webtoo.ls/api/v1/accounts/lookup?acct=ayo'
|
||||||
)
|
)
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
const { avatar } = data
|
const { avatar } = data
|
||||||
|
|
@ -51,13 +51,28 @@ const ogFileType = 'image/png'
|
||||||
font-size: var(--font-size-base);
|
font-size: var(--font-size-base);
|
||||||
color: var(--text-color-dark);
|
color: var(--text-color-dark);
|
||||||
background-color: var(--text-color-light);
|
background-color: var(--text-color-light);
|
||||||
|
|
||||||
|
background-image: radial-gradient(
|
||||||
|
circle,
|
||||||
|
hsl(var(--dot-grid-light)) 1px,
|
||||||
|
transparent 1px
|
||||||
|
);
|
||||||
|
background-size: 24px 24px;
|
||||||
|
background-position: -24px -24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
html,
|
html,
|
||||||
body,
|
body,
|
||||||
* {
|
* {
|
||||||
background: var(--bg-darker);
|
background-color: var(--bg-darker);
|
||||||
color: var(--text-color-light);
|
color: var(--text-color-light);
|
||||||
|
|
||||||
|
background-image: radial-gradient(
|
||||||
|
circle,
|
||||||
|
hsl(var(--dot-grid-dark)) 1px,
|
||||||
|
transparent 1px
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,4 +26,7 @@
|
||||||
--bg-dark: #343a40;
|
--bg-dark: #343a40;
|
||||||
--bg-darker: #212529;
|
--bg-darker: #212529;
|
||||||
--bg-darkest: #000;
|
--bg-darkest: #000;
|
||||||
|
|
||||||
|
--dot-grid-light: 214 32% 82%;
|
||||||
|
--dot-grid-dark: 215 25% 25%;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import { Picture } from 'astro:assets'
|
||||||
|
|
||||||
// fetch mastodon account
|
// fetch mastodon account
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
'https://social.ayco.io/api/v1/accounts/lookup?acct=ayo'
|
'https://m.webtoo.ls/api/v1/accounts/lookup?acct=ayo'
|
||||||
)
|
)
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
const { avatar, note } = data
|
const { avatar, note } = data
|
||||||
|
|
@ -38,13 +38,15 @@ const avatarSize = 150
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<a href="/now" class="now-wrapper action primary">
|
<a href="/now" class="now-wrapper action primary">
|
||||||
<span class="now-label">now</span>
|
<span class="now-label">
|
||||||
|
<status-indicator status="active" pulse> now </status-indicator>
|
||||||
|
</span>
|
||||||
<span class="status">{now.title}</span>
|
<span class="status">{now.title}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<main>
|
<main class=".dot-grid">
|
||||||
<section class="introduction-section">
|
<section class="introduction-section">
|
||||||
<p>
|
<p>
|
||||||
I care about the <em>Web</em>, and I love to <em>create</em> stuff to <em
|
I care about the <em>Web</em>, and I love to <em>create</em> stuff to <em
|
||||||
|
|
@ -93,6 +95,7 @@ const avatarSize = 150
|
||||||
|
|
||||||
&.primary {
|
&.primary {
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-style: dotted;
|
||||||
border-radius: 40px;
|
border-radius: 40px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import Footer from '../components/Footer.astro'
|
||||||
|
|
||||||
const title = 'PGP public key'
|
const title = 'PGP public key'
|
||||||
const description =
|
const description =
|
||||||
'Use to verify my digitally signed content like emails and files'
|
'Use to verify my digital signature or to encrypt messages intended only for me.'
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout title={"Ayo's " + title} description={description}>
|
<Layout title={"Ayo's " + title} description={description}>
|
||||||
|
|
@ -13,7 +13,12 @@ const description =
|
||||||
<p>{description}</p>
|
<p>{description}</p>
|
||||||
|
|
||||||
<div class="key-block" role="region" aria-labelledby="public-key">
|
<div class="key-block" role="region" aria-labelledby="public-key">
|
||||||
<button class="copy-btn" aria-label="Copy key to clipboard">Copy</button>
|
<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
|
<pre
|
||||||
id="public-key"><code>
|
id="public-key"><code>
|
||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
@ -31,32 +36,42 @@ AP0YLC768PFTBm9CM5T1BE0xjJ7s4dZSrVoI4n8RSe1nCA==
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
</code></pre>
|
</code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
<p>Expiry Date: 2027-05-30</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="/publickey.asc" class="download-link"> Download key (asc) </a>
|
Fingerprint: <code>17F1 3D5E 8FF7 372B 1354 5C38 65E6 BF64 1529 3C65</code
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
<p>This key will expire on: 2027-05-30</p>
|
|
||||||
|
<p></p>
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/* Clipboard copy logic */
|
/* Clipboard copy logic */
|
||||||
document.querySelector('.copy-btn').addEventListener('click', async () => {
|
const copyBtn = document.querySelector('.copy-btn')
|
||||||
const keyText = document.querySelector('#public-key code').innerText.trim()
|
if (copyBtn) {
|
||||||
try {
|
copyBtn.addEventListener('click', async () => {
|
||||||
await navigator.clipboard.writeText(keyText)
|
const keyText = (
|
||||||
const btn = document.querySelector('.copy-btn')
|
document.querySelector('#public-key code') as HTMLElement
|
||||||
const original = btn.textContent
|
)?.innerText.trim()
|
||||||
btn.textContent = 'Copied!'
|
if (!keyText) return
|
||||||
setTimeout(() => {
|
try {
|
||||||
btn.textContent = original
|
await navigator.clipboard.writeText(keyText)
|
||||||
}, 2000)
|
const btn = document.querySelector('.copy-btn')
|
||||||
} catch (err) {
|
if (btn) {
|
||||||
console.error('Copy failed', err)
|
const original = btn.textContent
|
||||||
alert('Unable to copy the key. Please copy it manually.')
|
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>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
@ -83,11 +98,16 @@ AP0YLC768PFTBm9CM5T1BE0xjJ7s4dZSrVoI4n8RSe1nCA==
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy button */
|
/* buttons */
|
||||||
.copy-btn {
|
.btn-wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0.5rem;
|
top: 0.5rem;
|
||||||
right: 0.5rem;
|
right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
background: var(--color-brand-blue-1);
|
background: var(--color-brand-blue-1);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border: none;
|
border: none;
|
||||||
|
|
@ -97,14 +117,15 @@ AP0YLC768PFTBm9CM5T1BE0xjJ7s4dZSrVoI4n8RSe1nCA==
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copy-btn:hover,
|
.btn:hover,
|
||||||
.copy-btn:focus {
|
.btn:focus {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copy-btn:focus {
|
.btn:focus {
|
||||||
outline: 2px solid #0056b3;
|
outline: 2px solid #0056b3;
|
||||||
outline-offset: 2px;
|
outline-offset: 2px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ import Card from '../components/Card.astro'
|
||||||
<main>
|
<main>
|
||||||
<h1><span class="text-gradient">Hobby Projects</span></h1>
|
<h1><span class="text-gradient">Hobby Projects</span></h1>
|
||||||
<p>
|
<p>
|
||||||
See more of my previous projects at <a href="https://ayos.blog">my blog</a
|
See more of my previous projects at <a href="https://ayos.blog/projects"
|
||||||
|
>my blog</a
|
||||||
>.
|
>.
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
||||||
14
vitest.config.ts
Normal file
14
vitest.config.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { defineConfig } from 'vitest/config'
|
||||||
|
|
||||||
|
// Exclude generated files and heavy folders from the watcher to avoid
|
||||||
|
// continuous re-runs when build output or other tools touch files.
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
globals: true,
|
||||||
|
environment: 'node',
|
||||||
|
exclude: ['dist/**', 'public/**', 'node_modules/**'],
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
exclude: ['dist/**', 'public/**', 'node_modules/**', '.git/**'],
|
||||||
|
},
|
||||||
|
})
|
||||||
Loading…
Reference in a new issue