Compare commits

..

12 Commits

Author SHA1 Message Date
4d80cef491 Add nodejs compatibility mode
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m6s
2025-02-03 17:55:28 +10:30
23f0d518c5 Rework email sending to be RFC5322 compliant by using mimetext package
Some checks failed
release / Publish to Cloudflare Pages (push) Failing after 58s
2025-02-03 17:53:57 +10:30
b543b57b4e Fix email formatting by removing leading spaces
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m4s
2025-02-03 17:38:44 +10:30
776088d62a Fix destination address in email message
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m4s
2025-02-03 17:36:22 +10:30
ffa8ceb563 Fix error handling in send email worker
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m7s
2025-02-03 17:27:03 +10:30
174996d572 Fix pages wrangler configuration file by specifying build directory
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m4s
2025-02-03 17:20:30 +10:30
8b5dfc68f3 Add default fetch method to worker
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m4s
2025-02-03 17:08:30 +10:30
d487f7ecb7 Add email worker and use it to send email from pages function
Some checks failed
release / Publish to Cloudflare Pages (push) Failing after 1m17s
2025-02-03 17:00:59 +10:30
73a4ee7df4 Update pages deploy action
Some checks failed
release / Publish to Cloudflare Pages (push) Failing after 55s
2025-02-03 16:13:14 +10:30
154ad1d9ea Fix email bining name
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m23s
2025-02-03 10:04:27 +10:30
8cf55aa0eb Wrap static forms plugin to retrieve email in context
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m20s
2025-02-03 09:55:18 +10:30
f2227f673e Try adding contact form with email
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m33s
2025-02-02 20:55:08 +10:30
15 changed files with 89 additions and 736 deletions

View File

@@ -19,7 +19,7 @@ jobs:
with: with:
node-version: 20 node-version: 20
- name: Install npm packages - name: Install pico
run: npm ci run: npm ci
- name: Create build artifacts - name: Create build artifacts

2
.gitignore vendored
View File

@@ -3,5 +3,3 @@ node_modules
.vscode .vscode
build build
.wrangler .wrangler
website.css
website.css.map

View File

@@ -2,25 +2,15 @@
A dead simple website showcasing my career and interests! A dead simple website showcasing my career and interests!
Also includes a contact form, which sends an email with relevant details from whoever is trying to make contact, using CloudFlare Workers and Email Routing.
## Build ## Build
Ensure npm is installed. Ensure npm is installed.
Download dependencies: Download pico css:
`npm install` `npm install`
Run `./build.sh` to build the site that is served by cloudflare pages
## Debugging ## Debugging
Easiest way to debug/visualise the content is to use the inbuilt IDE browser. VS Code/Codium can display a preview side-by-side by clicking the Open Preview to the Side button. Easiest way to debug/visualise the content is to use the inbuilt IDE browser. VS Code/Codium can display a preview side-by-side by clicking the Open Preview to the Side button.
This will show changes live, exactly as the content will be rendered when run from another webserver. This will show changes live, exactly as the content will be rendered when run from another webserver.
To generate the css file during development, run the following:
`npx sass --watch website.scss. website.css`
Note: The contact form cannot be tested locally with wrangler as this is not supported by Email Routing, instead you'll need to use the --remote

View File

@@ -1,3 +1,3 @@
/* /*
Content-Security-Policy: default-src 'self'; img-src 'self' data:; frame-ancestors 'none'; script-src static.cloudflareinsights.com; connect-src 'self' cloudflareinsights.com; Content-Security-Policy: default-src 'self'; frame-ancestors 'none'
X-Content-Type-Options: nosniff X-Content-Type-Options: nosniff

View File

@@ -1,3 +1,6 @@
mkdir -p build/ mkdir -p build/@picocss/pico/css/
cp -r *.png *.xml *.html *.svg *.webmanifest *.ico robots.txt _headers functions contact build cp -r *.png *.xml *.svg *.css *.webmanifest *.ico robots.txt _headers functions contact build
npx sass --quiet --style=compressed --no-source-map website.scss build/website.css # https://github.com/cloudflare/workers-sdk/issues/3615
sed 's/node_modules\///' index.html > build/index.html
sed 's/node_modules\///' contact/index.html > build/contact/index.html
cp node_modules/@picocss/pico/css/pico.min.css build/@picocss/pico/css/

View File

@@ -42,11 +42,15 @@ Mobile: ${formatEmptyString(mobile)}
Message: Message:
${message}`, ${message}`,
}); });
try {
const cfMessage = new EmailMessage( const cfMessage = new EmailMessage(
"contact@michaelpivato.dev", "contact@michaelpivato.dev",
"contact@michaelpivato.dev", "contact@michaelpivato.dev",
msg.asRaw() msg.asRaw()
); );
this.ctx.waitUntil(this.env.SEB.send(cfMessage)); await this.env.SEB.send(cfMessage);
} catch (e) {
throw e;
}
} }
} }

View File

@@ -1,5 +1,5 @@
// Generated by Wrangler by running `wrangler types` // Generated by Wrangler
// After adding bindings to `wrangler.json`, regenerate this interface via `npm run cf-typegen`
interface Env { interface Env {
SEB: SendEmail; SEB: SendEmail;
} }

View File

@@ -10,5 +10,5 @@
{ "name": "SEB", "destination_address": "contact@michaelpivato.dev" } { "name": "SEB", "destination_address": "contact@michaelpivato.dev" }
], ],
"workers_dev": false, "workers_dev": false,
"compatibility_flags": ["nodejs_compat"] "node_compat": true
} }

View File

@@ -4,7 +4,10 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Michael Pivato | Contact</title> <title>Michael Pivato | Contact</title>
<link rel="stylesheet" href="../website.css" /> <link
rel="stylesheet"
href="../node_modules/@picocss/pico/css/pico.min.css"
/>
<link <link
rel="apple-touch-icon" rel="apple-touch-icon"
sizes="180x180" sizes="180x180"
@@ -41,7 +44,7 @@
<main class="container"> <main class="container">
<header> <header>
<hgroup> <hgroup>
<h1>Contact</h1> <h1>Michael Pivato</h1>
<p>Send Michael a message</p> <p>Send Michael a message</p>
<a href="../">Back to resume</a> <a href="../">Back to resume</a>
</hgroup> </hgroup>
@@ -80,7 +83,7 @@
</label> </label>
<label> <label>
Message Message
<textarea name="message" placeholder="Message..."></textarea> <textarea name="message" placeholder="Mesage..."></textarea>
</label> </label>
</fieldset> </fieldset>
<input type="submit" value="Send Message" /> <input type="submit" value="Send Message" />

View File

@@ -29,15 +29,13 @@ export const onRequest: PagesFunction<Env> = (context) => {
// Must have some kind of identifiable information for me to actually care about them. // Must have some kind of identifiable information for me to actually care about them.
if ((fullName || email) && message) { if ((fullName || email) && message) {
try { try {
context.waitUntil( await context.env.SERVICE.sendEmail({
context.env.SERVICE.sendEmail({
fullName, fullName,
organisation, organisation,
email, email,
mobile, mobile,
message, message,
}) });
);
} catch (e) { } catch (e) {
return new Response(e); return new Response(e);
} }

View File

@@ -4,7 +4,8 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Michael Pivato</title> <title>Michael Pivato</title>
<link rel="stylesheet" href="website.css" /> <link rel="stylesheet" href="node_modules/@picocss/pico/css/pico.min.css" />
<link rel="stylesheet" href="site.css" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" /> <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" /> <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" /> <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
@@ -27,7 +28,7 @@
/> />
</head> </head>
<body> <body>
<main class="container responsive-nav"> <main class="container">
<aside> <aside>
<nav class="closed-on-mobile"> <nav class="closed-on-mobile">
<ul> <ul>
@@ -72,41 +73,6 @@
successfully implemented many applications that have been used and successfully implemented many applications that have been used and
loved by clients. loved by clients.
</p> </p>
<div class="special-card-wrapper">
<a href="#career" class="secondary special-card">
<article>
<header>Career</header>
</article>
</a>
<a href="#education" class="secondary special-card">
<article>
<header>Education</header>
</article>
</a>
<a href="#skills" class="secondary special-card">
<article>
<header>Skills</header>
</article>
</a>
<div class="special-card">
<article>
<header>
<a href="#hobbies" class="secondary">Hobby Projects</a>
</header>
<div>
<a href="#bufpiv">
<button class="contrast">Buf Piv</button>
</a>
<a href="#picar">
<button class="contrast">PiCar</button>
</a>
<a href="#depthprediction">
<button class="contrast">Depth Prediction</button>
</a>
</div>
</article>
</div>
</div>
<section id="career"> <section id="career">
<h2>Career</h2> <h2>Career</h2>
<hgroup id="telstrahealth"> <hgroup id="telstrahealth">
@@ -301,39 +267,29 @@
<p> <p>
Over the years I've hacked away at various personal projects. My Over the years I've hacked away at various personal projects. My
preference is always to build, run and host applications locally, preference is always to build, run and host applications locally,
however I have come around to cloud services for public-facing which includes this page!
resources, such as CloudFlare, which is used to host this page!
</p> </p>
<p> <p>
I have used AI/ML in the past, as seen in my own Depth Prediction Recently my interesets have shifted slightly to large machine
implementation, and LLMs, where I fine-tuned BERT to perform Named learning models, and have messed around with Stable Diffusion
Entity Recognition, however recent models have gotten too large to (mainly with
train at home. I also use local LLMs in LM Studio, to provide basic <a href="https://github.com/invoke-ai">Invoke AI</a>) and Large
information and coding assistance when learning a new framework. Language Models such as the
Recently my interesets have shifted to designing applications that <a href="https://llama.meta.com">Llama</a> family. I have also
can maximise throughput for large datasets and minimise response trained/finetuned LLMs in the past (BERT), however this has been
time for queries/charts. I'm currently reading outside of my capability recently due to the growth in parameters.
<a
href="https://www.oreilly.com/library/view/designing-data-intensive-applications/9781491903063/"
>Designing Data-Intensive Applications</a
>
to facilitate improvements in the Ingey project once core
implemetation is complete.
</p> </p>
<p> <p>
Finally I've enjoyed writing new applications in Rust; the Finally I've thoroughly enjoyed writing in Rust, mainly the
efficiency, ease of use and correctness have been fantastic. One efficiency, ease of use and correctness that come from using this
example is in the programming language. One example was in the
<a href="https://gitea.michaelpivato.dev/vato007/ingey">Ingey</a> <a href="https://gitea.michaelpivato.dev/vato007/ingey">Ingey</a>
project, where I reduced the time to perform reciprocal accounting project, where I reduced the time taken for processing some demo
on a costing product from ~1.5 hours to ~7 seconds on a data on the costing product from ~1.5 hours to ~7 seconds on a
laptop/desktop, or ~36 seconds on a smartphone. This was due to laptop/desktop, or ~36 seconds on a smartphone. This was mainly due
avoiding non-bulk inserts into a relational database, and using a to not using SQL Server, and using a custom algorithm in overhead
custom algorithm in overhead allocation that significantly reduced allocation that significantly reduced memory consumption and the
memory consumption and the number of required calculations. The number of required calculations.
optimisations applied by Rust in release mode also had a significant
impact on performance, and is what facilitated easy deployment to an
iOS application.
</p> </p>
<hgroup id="bufpiv"> <hgroup id="bufpiv">
<h3>Buf Piv</h3> <h3>Buf Piv</h3>
@@ -344,10 +300,10 @@
</p> </p>
</hgroup> </hgroup>
<p> <p>
This is a Tauri + Angular application that makes it easy to edit This is a tauri application that makes it easy to edit json files
json files conforming to a protobuf definition. It works as a conforming to a protobuf definition. It works as a standalone
standalone desktop application for the most complete experience, desktop application for the most complete experience, with browser
with browser support to show Tauri's versatility as well. support to show tauri's versatility as well.
</p> </p>
<p> <p>
A browser demo is available at A browser demo is available at
@@ -363,7 +319,7 @@
This project originally involved communication between a Raspberry This project originally involved communication between a Raspberry
Pi and a Traxxas Slash using the Pi's GPIO to control the steering Pi and a Traxxas Slash using the Pi's GPIO to control the steering
and throttle of the RC Car. This was mounted on some 3D printed and throttle of the RC Car. This was mounted on some 3D printed
brackets. The steering and throttle are set by an iPhone/Android brackets.The steering and throttle are set using an iPhone/Android
application connected over WiFi. application connected over WiFi.
</p> </p>
<p>Over time this worked as a base to explore other ideas, namely:</p> <p>Over time this worked as a base to explore other ideas, namely:</p>
@@ -385,8 +341,8 @@
<p> <p>
Recently there have been efforts to port the backend to Rust, with Recently there have been efforts to port the backend to Rust, with
the 2D Lidar sensing and control completed. The Python BreezySLAM the 2D Lidar sensing and control completed. The Python BreezySLAM
implementation is currently unfinished, mainly due to work on other implementation is currently unfinished, mainly due to distractions
projects from other projects
</p> </p>
<hgroup id="depthprediction"> <hgroup id="depthprediction">
<h3>Depth Prediction</h3> <h3>Depth Prediction</h3>

508
package-lock.json generated
View File

@@ -15,7 +15,6 @@
}, },
"devDependencies": { "devDependencies": {
"@cloudflare/workers-types": "^4.20250129.0", "@cloudflare/workers-types": "^4.20250129.0",
"sass": "^1.85.0",
"typescript": "^5.7.3", "typescript": "^5.7.3",
"wrangler": "^3.107.2" "wrangler": "^3.107.2"
} }
@@ -566,316 +565,6 @@
"@jridgewell/sourcemap-codec": "^1.4.10" "@jridgewell/sourcemap-codec": "^1.4.10"
} }
}, },
"node_modules/@parcel/watcher": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
"integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"dependencies": {
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
"micromatch": "^4.0.5",
"node-addon-api": "^7.0.0"
},
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"@parcel/watcher-android-arm64": "2.5.1",
"@parcel/watcher-darwin-arm64": "2.5.1",
"@parcel/watcher-darwin-x64": "2.5.1",
"@parcel/watcher-freebsd-x64": "2.5.1",
"@parcel/watcher-linux-arm-glibc": "2.5.1",
"@parcel/watcher-linux-arm-musl": "2.5.1",
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
"@parcel/watcher-linux-arm64-musl": "2.5.1",
"@parcel/watcher-linux-x64-glibc": "2.5.1",
"@parcel/watcher-linux-x64-musl": "2.5.1",
"@parcel/watcher-win32-arm64": "2.5.1",
"@parcel/watcher-win32-ia32": "2.5.1",
"@parcel/watcher-win32-x64": "2.5.1"
}
},
"node_modules/@parcel/watcher-android-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
"integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
"integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
"integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-freebsd-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
"integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
"integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
"integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
"integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
"integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
"integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
"integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
"integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-ia32": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
"integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
"integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@picocss/pico": { "node_modules/@picocss/pico": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/@picocss/pico/-/pico-2.0.4.tgz", "resolved": "https://registry.npmjs.org/@picocss/pico/-/pico-2.0.4.tgz",
@@ -923,36 +612,6 @@
"integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==",
"dev": true "dev": true
}, },
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/confbox": { "node_modules/confbox": {
"version": "0.1.8", "version": "0.1.8",
"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz",
@@ -990,20 +649,6 @@
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
"dev": true "dev": true
}, },
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"dev": true,
"license": "Apache-2.0",
"optional": true,
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/esbuild": { "node_modules/esbuild": {
"version": "0.17.19", "version": "0.17.19",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz",
@@ -1071,20 +716,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/fsevents": { "node_modules/fsevents": {
"version": "2.3.3", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -1115,49 +746,6 @@
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
"dev": true "dev": true
}, },
"node_modules/immutable": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz",
"integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==",
"dev": true,
"license": "MIT"
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/js-base64": { "node_modules/js-base64": {
"version": "3.7.7", "version": "3.7.7",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz",
@@ -1172,21 +760,6 @@
"sourcemap-codec": "^1.4.8" "sourcemap-codec": "^1.4.8"
} }
}, },
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/mime": { "node_modules/mime": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
@@ -1285,14 +858,6 @@
"mustache": "bin/mustache" "mustache": "bin/mustache"
} }
}, },
"node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/ohash": { "node_modules/ohash": {
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.4.tgz", "resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.4.tgz",
@@ -1311,20 +876,6 @@
"integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
"dev": true "dev": true
}, },
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pkg-types": { "node_modules/pkg-types": {
"version": "1.3.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz",
@@ -1348,20 +899,6 @@
"integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==", "integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==",
"dev": true "dev": true
}, },
"node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/regenerator-runtime": { "node_modules/regenerator-runtime": {
"version": "0.14.1", "version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
@@ -1397,27 +934,6 @@
"estree-walker": "^0.6.1" "estree-walker": "^0.6.1"
} }
}, },
"node_modules/sass": {
"version": "1.85.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.85.0.tgz",
"integrity": "sha512-3ToiC1xZ1Y8aU7+CkgCI/tqyuPXEmYGJXO7H4uqp0xkLXUqp88rQQ4j1HmP37xSJLbCJPaIiv+cT1y+grssrww==",
"dev": true,
"license": "MIT",
"dependencies": {
"chokidar": "^4.0.0",
"immutable": "^5.0.2",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=14.0.0"
},
"optionalDependencies": {
"@parcel/watcher": "^2.4.1"
}
},
"node_modules/source-map": { "node_modules/source-map": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -1427,16 +943,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/sourcemap-codec": { "node_modules/sourcemap-codec": {
"version": "1.4.8", "version": "1.4.8",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
@@ -1464,20 +970,6 @@
"npm": ">=6" "npm": ">=6"
} }
}, },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/typescript": { "node_modules/typescript": {
"version": "5.7.3", "version": "5.7.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",

View File

@@ -17,7 +17,6 @@
}, },
"devDependencies": { "devDependencies": {
"@cloudflare/workers-types": "^4.20250129.0", "@cloudflare/workers-types": "^4.20250129.0",
"sass": "^1.85.0",
"typescript": "^5.7.3", "typescript": "^5.7.3",
"wrangler": "^3.107.2" "wrangler": "^3.107.2"
} }

24
site.css Normal file
View File

@@ -0,0 +1,24 @@
.closed-on-mobile {
display: none;
}
@media (min-width: 992px) {
main {
--block-spacing-horizontal: calc(var(--spacing) * 1.75);
grid-column-gap: calc(var(--block-spacing-horizontal) * 3);
display: grid;
grid-template-columns: 200px auto;
}
.closed-on-mobile {
display: block;
}
main > aside nav {
position: fixed;
width: 200px;
max-height: calc(100vh - 5.5rem);
overflow-x: hidden;
overflow-y: auto;
}
}

View File

@@ -1,114 +0,0 @@
@use "node_modules/@picocss/pico/scss/pico" with (
$theme-color: "cyan",
$modules: (
"content/code": false,
"content/embedded": true,
"content/figure": false,
"content/miscs": false,
"content/table": false,
"forms/checkbox-radio-switch": false,
"forms/input-color": false,
"forms/input-date": false,
"forms/input-file": false,
"forms/input-range": false,
"forms/input-search": false,
"components/card": true,
"components/dropdown": false,
"components/loading": false,
"components/group": false,
"components/modal": false,
"components/progress": false,
"components/tooltip": false,
"layout/grid": true,
"layout/landmarks": false,
"layout/overflow-auto": false,
"utilities/accessibility": false,
"utilities/reduce-motion": false,
)
);
@use "node_modules/@picocss/pico/scss/colors" as *;
.closed-on-mobile {
display: none;
}
$animation-length: 0.4s;
.special-card {
filter: drop-shadow(0 0 1rem $cyan-500);
margin: 1.5rem;
cursor: pointer;
transition: scale $animation-length;
opacity: 0;
min-width: 9rem;
max-width: 9rem;
animation: fadeIn 1s forwards;
@for $i from 1 through 4 {
&:nth-child(#{$i}n) {
// TODO: Delay last stuff less than earlier stuff
animation-delay: #{$i * 0.1}s;
}
}
&:hover {
scale: 1.02;
& > article > div {
// Set to something larger than button
// since % doesn't work
max-height: 400px;
height: fit-content;
}
}
button {
width: 100%;
margin: 0.5rem 0;
}
& > article > div {
display: block;
max-height: 0;
overflow: hidden;
// Close it faster
transition: max-height 0.2 * $animation-length;
}
&:hover > article > div {
transition: max-height $animation-length;
}
}
.special-card-wrapper {
display: flex;
flex-wrap: wrap;
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@media (min-width: 992px) {
main.responsive-nav {
--block-spacing-horizontal: calc(var(--spacing) * 1.75);
grid-column-gap: calc(var(--block-spacing-horizontal) * 3);
display: grid;
grid-template-columns: 200px auto;
}
.closed-on-mobile {
display: block;
}
main > aside nav {
position: fixed;
width: 200px;
max-height: calc(100vh - 5.5rem);
overflow-x: hidden;
overflow-y: auto;
}
}