Compare commits
14 Commits
4d80cef491
...
cards
| Author | SHA1 | Date | |
|---|---|---|---|
| a0e06403de | |||
| 7788da14e5 | |||
| 5c4da5aaed | |||
| 1e57e36dea | |||
| 639be72f4b | |||
| c873e0e80f | |||
| d117841faf | |||
| 75d5ed73dc | |||
| a8b511dabc | |||
| f8e4f93c94 | |||
| 80244e2d88 | |||
| 7b6b0fafa9 | |||
| 3f575a0e4c | |||
| e0c12292cd |
@@ -2,7 +2,6 @@ name: release
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
@@ -17,20 +16,28 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
lfs: true
|
lfs: true
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
|
||||||
- name: Install pico
|
- name: Install npm packages
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Create build artifacts
|
- name: Create build artifacts
|
||||||
run: ./build.sh
|
run: ./build.sh
|
||||||
|
|
||||||
- name: Publish to Cloudflare Pages
|
- name: Publish to Cloudflare Pages
|
||||||
uses: cloudflare/pages-action@v1
|
uses: cloudflare/wrangler-action@v3
|
||||||
with:
|
with:
|
||||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
projectName: resume
|
command: pages deploy build --project-name=resume
|
||||||
directory: build
|
|
||||||
|
- name: Publish Email Worker
|
||||||
|
uses: cloudflare/wrangler-action@v3
|
||||||
|
with:
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
workingDirectory: contact-email-worker
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
|
|||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,4 +1,7 @@
|
|||||||
/node_modules
|
node_modules
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.vscode
|
.vscode
|
||||||
build
|
build
|
||||||
|
.wrangler
|
||||||
|
website.css
|
||||||
|
website.css.map
|
||||||
12
README.md
12
README.md
@@ -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
|
||||||
|
|||||||
2
_headers
2
_headers
@@ -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
|
||||||
8
build.sh
8
build.sh
@@ -1,5 +1,3 @@
|
|||||||
mkdir -p build/@picocss/pico/css/
|
mkdir -p build/
|
||||||
cp *.png *.xml *.svg *.css *.webmanifest *.ico robots.txt _headers 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
|
|
||||||
cp node_modules/@picocss/pico/css/pico.min.css build/@picocss/pico/css/
|
|
||||||
2446
contact-email-worker/package-lock.json
generated
Normal file
2446
contact-email-worker/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
contact-email-worker/package.json
Normal file
20
contact-email-worker/package.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "royal-leaf-c03c",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"deploy": "wrangler deploy",
|
||||||
|
"dev": "wrangler dev",
|
||||||
|
"start": "wrangler dev",
|
||||||
|
"cf-typegen": "wrangler types"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@cloudflare/vitest-pool-workers": "^0.6.4",
|
||||||
|
"@cloudflare/workers-types": "^4.20250129.0",
|
||||||
|
"typescript": "^5.5.2",
|
||||||
|
"wrangler": "^3.107.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mimetext": "^3.0.27"
|
||||||
|
}
|
||||||
|
}
|
||||||
52
contact-email-worker/src/index.ts
Normal file
52
contact-email-worker/src/index.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { EmailMessage } from "cloudflare:email";
|
||||||
|
import { WorkerEntrypoint } from "cloudflare:workers";
|
||||||
|
import { createMimeMessage } from "mimetext";
|
||||||
|
|
||||||
|
const formatEmptyString = (s: string) => s ?? "Not Specified";
|
||||||
|
|
||||||
|
interface EmailDetails {
|
||||||
|
fullName: string;
|
||||||
|
organisation: string;
|
||||||
|
email: string;
|
||||||
|
mobile: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class SendEmailWorker extends WorkerEntrypoint<Env> {
|
||||||
|
async fetch() {
|
||||||
|
return new Response("Unimplemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendEmail({
|
||||||
|
fullName,
|
||||||
|
organisation,
|
||||||
|
email,
|
||||||
|
mobile,
|
||||||
|
message,
|
||||||
|
}: EmailDetails) {
|
||||||
|
const msg = createMimeMessage();
|
||||||
|
msg.setSender({
|
||||||
|
name: "Michael Pivato Contact Form",
|
||||||
|
addr: "contact@michaelpivato.dev",
|
||||||
|
});
|
||||||
|
msg.setRecipient("contact@michaelpivato.dev");
|
||||||
|
msg.setSubject(`Message from ${fullName ?? email}`);
|
||||||
|
msg.addMessage({
|
||||||
|
contentType: "text/plain",
|
||||||
|
data: `You've received a new message from ${fullName ?? email}.
|
||||||
|
Full Name: ${formatEmptyString(fullName)}
|
||||||
|
Organisation: ${formatEmptyString(organisation)}
|
||||||
|
Email: ${formatEmptyString(email)}
|
||||||
|
Mobile: ${formatEmptyString(mobile)}
|
||||||
|
|
||||||
|
Message:
|
||||||
|
${message}`,
|
||||||
|
});
|
||||||
|
const cfMessage = new EmailMessage(
|
||||||
|
"contact@michaelpivato.dev",
|
||||||
|
"contact@michaelpivato.dev",
|
||||||
|
msg.asRaw()
|
||||||
|
);
|
||||||
|
this.ctx.waitUntil(this.env.SEB.send(cfMessage));
|
||||||
|
}
|
||||||
|
}
|
||||||
46
contact-email-worker/tsconfig.json
Normal file
46
contact-email-worker/tsconfig.json
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||||
|
|
||||||
|
/* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||||
|
"target": "es2021",
|
||||||
|
/* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||||
|
"lib": ["es2021"],
|
||||||
|
/* Specify what JSX code is generated. */
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
|
/* Specify what module code is generated. */
|
||||||
|
"module": "es2022",
|
||||||
|
/* Specify how TypeScript looks up a file from a given module specifier. */
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
/* Specify type package names to be included without being referenced in a source file. */
|
||||||
|
"types": [
|
||||||
|
"@cloudflare/workers-types/2023-07-01"
|
||||||
|
],
|
||||||
|
/* Enable importing .json files */
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
|
||||||
|
/* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
|
||||||
|
"allowJs": true,
|
||||||
|
/* Enable error reporting in type-checked JavaScript files. */
|
||||||
|
"checkJs": false,
|
||||||
|
|
||||||
|
/* Disable emitting files from a compilation. */
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
/* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||||
|
"isolatedModules": true,
|
||||||
|
/* Allow 'import x from y' when a module doesn't have a default export. */
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
/* Ensure that casing is correct in imports. */
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
|
||||||
|
/* Enable all strict type-checking options. */
|
||||||
|
"strict": true,
|
||||||
|
|
||||||
|
/* Skip type checking all .d.ts files. */
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"exclude": ["test"],
|
||||||
|
"include": ["worker-configuration.d.ts", "src/**/*.ts"]
|
||||||
|
}
|
||||||
5
contact-email-worker/worker-configuration.d.ts
vendored
Normal file
5
contact-email-worker/worker-configuration.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// Generated by Wrangler by running `wrangler types`
|
||||||
|
|
||||||
|
interface Env {
|
||||||
|
SEB: SendEmail;
|
||||||
|
}
|
||||||
14
contact-email-worker/wrangler.json
Normal file
14
contact-email-worker/wrangler.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"$schema": "node_modules/wrangler/config-schema.json",
|
||||||
|
"name": "contact-email",
|
||||||
|
"main": "src/index.ts",
|
||||||
|
"compatibility_date": "2025-01-29",
|
||||||
|
"observability": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"send_email": [
|
||||||
|
{ "name": "SEB", "destination_address": "contact@michaelpivato.dev" }
|
||||||
|
],
|
||||||
|
"workers_dev": false,
|
||||||
|
"compatibility_flags": ["nodejs_compat"]
|
||||||
|
}
|
||||||
93
contact/index.html
Normal file
93
contact/index.html
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>Michael Pivato | Contact</title>
|
||||||
|
<link rel="stylesheet" href="../website.css" />
|
||||||
|
<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="16x16"
|
||||||
|
href="../favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
<link rel="manifest" href="../site.webmanifest" />
|
||||||
|
<link rel="mask-icon" href="../safari-pinned-tab.svg" color="#5bbad5" />
|
||||||
|
<meta name="msapplication-TileColor" content="#da532c" />
|
||||||
|
<meta
|
||||||
|
name="theme-color"
|
||||||
|
media="(prefers-color-scheme: dark)"
|
||||||
|
content="#13171f"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="theme-color"
|
||||||
|
media="(prefers-color-scheme: light)"
|
||||||
|
content="#2a3140"
|
||||||
|
/>
|
||||||
|
<meta name="description" content="Michael Pivato's Resume: Contact Form" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="container">
|
||||||
|
<header>
|
||||||
|
<hgroup>
|
||||||
|
<h1>Contact</h1>
|
||||||
|
<p>Send Michael a message</p>
|
||||||
|
<a href="../">Back to resume</a>
|
||||||
|
</hgroup>
|
||||||
|
</header>
|
||||||
|
<form data-static-form-name="contact">
|
||||||
|
<fieldset>
|
||||||
|
<label>
|
||||||
|
Name
|
||||||
|
<input name="name" placeholder="Name" autocomplete="name" />
|
||||||
|
</label>
|
||||||
|
<label
|
||||||
|
>Organisation
|
||||||
|
<input
|
||||||
|
name="org"
|
||||||
|
placeholder="Organisation"
|
||||||
|
autocomplete="organization"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Email
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
name="email"
|
||||||
|
placeholder="Email"
|
||||||
|
autocomplete="email"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Mobile
|
||||||
|
<input
|
||||||
|
type="tel"
|
||||||
|
name="mobile"
|
||||||
|
placeholder="Mobile"
|
||||||
|
autocomplete="mobile"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Message
|
||||||
|
<textarea name="message" placeholder="Message..."></textarea>
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
<input type="submit" value="Send Message" />
|
||||||
|
</form>
|
||||||
|
<footer class="container">
|
||||||
|
<small>Michael Pivato • 2025</small>
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
49
functions/contact.ts
Normal file
49
functions/contact.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import staticFormsPlugin from "@cloudflare/pages-plugin-static-forms";
|
||||||
|
|
||||||
|
interface EmailDetails {
|
||||||
|
fullName: string;
|
||||||
|
organisation: string;
|
||||||
|
email: string;
|
||||||
|
mobile: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SendEmailWorker {
|
||||||
|
sendEmail(rawMessage: EmailDetails): Promise<Response>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Env {
|
||||||
|
SERVICE: SendEmailWorker;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const onRequest: PagesFunction<Env> = (context) => {
|
||||||
|
// Wrap static forms plugin so we can extract the env to use email routing
|
||||||
|
return staticFormsPlugin({
|
||||||
|
respondWith: async ({ formData }) => {
|
||||||
|
const fullName = formData.get("name");
|
||||||
|
const organisation = formData.get("org");
|
||||||
|
const email = formData.get("email");
|
||||||
|
const mobile = formData.get("mobile");
|
||||||
|
const message = formData.get("message");
|
||||||
|
|
||||||
|
// Must have some kind of identifiable information for me to actually care about them.
|
||||||
|
if ((fullName || email) && message) {
|
||||||
|
try {
|
||||||
|
context.waitUntil(
|
||||||
|
context.env.SERVICE.sendEmail({
|
||||||
|
fullName,
|
||||||
|
organisation,
|
||||||
|
email,
|
||||||
|
mobile,
|
||||||
|
message,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
return new Response(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.redirect("https://michaelpivato.dev");
|
||||||
|
},
|
||||||
|
})(context);
|
||||||
|
};
|
||||||
13
functions/tsconfig.json
Normal file
13
functions/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "esnext",
|
||||||
|
"module": "NodeNext",
|
||||||
|
"moduleResolution": "nodenext",
|
||||||
|
"lib": [
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
|
"types": [
|
||||||
|
"@cloudflare/workers-types"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
101
index.html
101
index.html
@@ -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>
|
||||||
@@ -63,6 +62,7 @@
|
|||||||
<hgroup>
|
<hgroup>
|
||||||
<h1>Michael Pivato</h1>
|
<h1>Michael Pivato</h1>
|
||||||
<p>Career summary and interests</p>
|
<p>Career summary and interests</p>
|
||||||
|
<a href="contact">Contact</a>
|
||||||
</hgroup>
|
</hgroup>
|
||||||
</header>
|
</header>
|
||||||
<p>
|
<p>
|
||||||
@@ -72,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">
|
||||||
@@ -266,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>
|
||||||
@@ -299,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
|
||||||
@@ -318,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>
|
||||||
@@ -340,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>
|
||||||
|
|||||||
1600
package-lock.json
generated
1600
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@@ -7,9 +7,18 @@
|
|||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
|
"type": "module",
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@picocss/pico": "^2.0.0"
|
"@cloudflare/pages-plugin-static-forms": "^1.0.3",
|
||||||
|
"@picocss/pico": "^2.0.0",
|
||||||
|
"mimetext": "^3.0.27"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@cloudflare/workers-types": "^4.20250129.0",
|
||||||
|
"sass": "^1.85.0",
|
||||||
|
"typescript": "^5.7.3",
|
||||||
|
"wrangler": "^3.107.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
24
site.css
24
site.css
@@ -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
114
website.scss
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
7
wrangler.json
Normal file
7
wrangler.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"$schema": "node_modules/wrangler/config-schema.json",
|
||||||
|
"name": "resume",
|
||||||
|
"compatibility_date": "2025-01-29",
|
||||||
|
"services": [{ "binding": "SERVICE", "service": "contact-email" }],
|
||||||
|
"pages_build_output_dir": "build"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user