Compare commits

..

14 Commits

Author SHA1 Message Date
a0e06403de Add initial cards concept
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m15s
2025-04-23 16:53:33 +09:30
7788da14e5 Remove redundant try catch
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m37s
2025-04-23 14:58:12 +09:30
5c4da5aaed Rename site.css to website.css
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m22s
2025-02-20 16:51:06 +10:30
1e57e36dea Reduce css bundle size, switch to cyan colour theme (#4)
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m8s
Reviewed-on: #4
2025-02-20 16:34:27 +10:30
639be72f4b Add self to connect-src csp header
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m30s
2025-02-19 21:32:01 +10:30
c873e0e80f Allow cloudflare analytics through csp header
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m39s
2025-02-19 21:20:49 +10:30
d117841faf Fix typo, rename contact form heading to Contact
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m26s
2025-02-16 21:23:09 +10:30
75d5ed73dc Immediately return in pages function when sending email through contact form
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m5s
2025-02-13 22:45:02 +10:30
a8b511dabc Skip waiting on email to send when calling worker
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m2s
2025-02-13 22:29:37 +10:30
f8e4f93c94 Allow inline SVG icons through CSP (#2)
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m2s
Reviewed-on: #2
2025-02-12 15:23:56 +10:30
80244e2d88 Update hobby projects, fix grammar in hobby projects
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m7s
2025-02-10 10:02:27 +10:30
7b6b0fafa9 Re-add wait on email to send as otherwise cloudflare triggers a disconnect and stops the worker
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m8s
2025-02-04 17:01:09 +10:30
3f575a0e4c Stop waiting for email to send when handling contact form submission
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m32s
2025-02-04 16:48:33 +10:30
e0c12292cd Add a contact form to the resume (#1)
All checks were successful
release / Publish to Cloudflare Pages (push) Successful in 1m6s
Reviewed-on: #1
2025-02-03 18:10:09 +10:30
15 changed files with 736 additions and 89 deletions

View File

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

2
.gitignore vendored
View File

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

View File

@@ -2,15 +2,25 @@
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 pico css: Download dependencies:
`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'; frame-ancestors 'none' Content-Security-Policy: default-src 'self'; img-src 'self' data:; frame-ancestors 'none'; script-src static.cloudflareinsights.com; connect-src 'self' cloudflareinsights.com;
X-Content-Type-Options: nosniff X-Content-Type-Options: nosniff

View File

@@ -1,6 +1,3 @@
mkdir -p build/@picocss/pico/css/ mkdir -p build/
cp -r *.png *.xml *.svg *.css *.webmanifest *.ico robots.txt _headers functions contact build cp -r *.png *.xml *.html *.svg *.webmanifest *.ico robots.txt _headers functions contact build
# https://github.com/cloudflare/workers-sdk/issues/3615 npx sass --quiet --style=compressed --no-source-map website.scss build/website.css
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,15 +42,11 @@ 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 // Generated by Wrangler by running `wrangler types`
// 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,
"node_compat": true "compatibility_flags": ["nodejs_compat"]
} }

View File

@@ -4,10 +4,7 @@
<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 <link rel="stylesheet" href="../website.css" />
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"
@@ -44,7 +41,7 @@
<main class="container"> <main class="container">
<header> <header>
<hgroup> <hgroup>
<h1>Michael Pivato</h1> <h1>Contact</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>
@@ -83,7 +80,7 @@
</label> </label>
<label> <label>
Message Message
<textarea name="message" placeholder="Mesage..."></textarea> <textarea name="message" placeholder="Message..."></textarea>
</label> </label>
</fieldset> </fieldset>
<input type="submit" value="Send Message" /> <input type="submit" value="Send Message" />

View File

@@ -29,13 +29,15 @@ 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 {
await context.env.SERVICE.sendEmail({ context.waitUntil(
fullName, context.env.SERVICE.sendEmail({
organisation, fullName,
email, organisation,
mobile, email,
message, mobile,
}); message,
})
);
} catch (e) { } catch (e) {
return new Response(e); return new Response(e);
} }

View File

@@ -4,8 +4,7 @@
<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="node_modules/@picocss/pico/css/pico.min.css" /> <link rel="stylesheet" href="website.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" />
@@ -28,7 +27,7 @@
/> />
</head> </head>
<body> <body>
<main class="container"> <main class="container responsive-nav">
<aside> <aside>
<nav class="closed-on-mobile"> <nav class="closed-on-mobile">
<ul> <ul>
@@ -73,6 +72,41 @@
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">
@@ -267,29 +301,39 @@
<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,
which includes this page! however I have come around to cloud services for public-facing
resources, such as CloudFlare, which is used to host this page!
</p> </p>
<p> <p>
Recently my interesets have shifted slightly to large machine I have used AI/ML in the past, as seen in my own Depth Prediction
learning models, and have messed around with Stable Diffusion implementation, and LLMs, where I fine-tuned BERT to perform Named
(mainly with Entity Recognition, however recent models have gotten too large to
<a href="https://github.com/invoke-ai">Invoke AI</a>) and Large train at home. I also use local LLMs in LM Studio, to provide basic
Language Models such as the information and coding assistance when learning a new framework.
<a href="https://llama.meta.com">Llama</a> family. I have also Recently my interesets have shifted to designing applications that
trained/finetuned LLMs in the past (BERT), however this has been can maximise throughput for large datasets and minimise response
outside of my capability recently due to the growth in parameters. time for queries/charts. I'm currently reading
<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 thoroughly enjoyed writing in Rust, mainly the Finally I've enjoyed writing new applications in Rust; the
efficiency, ease of use and correctness that come from using this efficiency, ease of use and correctness have been fantastic. One
programming language. One example was in the example is 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 taken for processing some demo project, where I reduced the time to perform reciprocal accounting
data on the costing product from ~1.5 hours to ~7 seconds on a on a costing product from ~1.5 hours to ~7 seconds on a
laptop/desktop, or ~36 seconds on a smartphone. This was mainly due laptop/desktop, or ~36 seconds on a smartphone. This was due to
to not using SQL Server, and using a custom algorithm in overhead avoiding non-bulk inserts into a relational database, and using a
allocation that significantly reduced memory consumption and the custom algorithm in overhead allocation that significantly reduced
number of required calculations. memory consumption and the number of required calculations. The
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>
@@ -300,10 +344,10 @@
</p> </p>
</hgroup> </hgroup>
<p> <p>
This is a tauri application that makes it easy to edit json files This is a Tauri + Angular application that makes it easy to edit
conforming to a protobuf definition. It works as a standalone json files conforming to a protobuf definition. It works as a
desktop application for the most complete experience, with browser standalone desktop application for the most complete experience,
support to show tauri's versatility as well. with browser support to show Tauri's versatility as well.
</p> </p>
<p> <p>
A browser demo is available at A browser demo is available at
@@ -319,7 +363,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 using an iPhone/Android brackets. The steering and throttle are set by 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>
@@ -341,8 +385,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 distractions implementation is currently unfinished, mainly due to work on other
from other projects 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,6 +15,7 @@
}, },
"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"
} }
@@ -565,6 +566,316 @@
"@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",
@@ -612,6 +923,36 @@
"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",
@@ -649,6 +990,20 @@
"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",
@@ -716,6 +1071,20 @@
"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",
@@ -746,6 +1115,49 @@
"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",
@@ -760,6 +1172,21 @@
"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",
@@ -858,6 +1285,14 @@
"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",
@@ -876,6 +1311,20 @@
"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",
@@ -899,6 +1348,20 @@
"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",
@@ -934,6 +1397,27 @@
"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",
@@ -943,6 +1427,16 @@
"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",
@@ -970,6 +1464,20 @@
"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,6 +17,7 @@
}, },
"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"
} }

View File

@@ -1,24 +0,0 @@
.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;
}
}

114
website.scss Normal file
View File

@@ -0,0 +1,114 @@
@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;
}
}