diff --git a/bun.lock b/bun.lock index 9e023f6..a08ed2e 100644 --- a/bun.lock +++ b/bun.lock @@ -5,8 +5,9 @@ "": { "name": "dan", "dependencies": { - "98se.css": "../98se.css", + "98se.css": "/err/98se.css", "@danielx/civet": "^0.11.7", + "@fontsource/roboto-condensed": "^5.2.8", "@hypeup/client": "workspace:*", "@hypeup/lexicon": "workspace:*", "@hypeup/runtime": "workspace:*", @@ -19,6 +20,7 @@ "hypeup": "workspace:*", "oxfmt": "^0.47.0", "oxlint": "^1.62.0", + "playwright": "^1.54.1", }, }, "../../sig/hypeup/packages/babel": { @@ -92,7 +94,6 @@ "@webref/elements": "^2.7.1", "css-tree": "^3.2.1", "html-tags": "^5.1.0", - "known-css-properties": "^0.37.0", }, "devDependencies": { "@types/bun": "^1.3.13", @@ -158,7 +159,7 @@ }, }, "packages": { - "98se.css": ["98.css@file:../98se.css", { "devDependencies": { "chokidar": "^3.3.1", "cssnano": "^5.0.1", "dedent": "^0.7.0", "ejs": "^3.0.2", "gh-pages": "^2.2.0", "glob": "^7.1.6", "highlight.js": "^10.4.1", "live-server": "^1.2.1", "mkdirp": "^1.0.4", "postcss": "^8.2.12", "postcss-calc": "^7.0.2", "postcss-copy": "^7.1.0", "postcss-css-variables": "^0.14.0", "postcss-inline": "^1.2.0", "postcss-inline-svg": "^4.1.0" } }], + "98se.css": ["98.css@file:../../err/98se.css", { "devDependencies": { "chokidar": "^3.3.1", "cssnano": "^5.0.1", "dedent": "^0.7.0", "ejs": "^3.0.2", "gh-pages": "^2.2.0", "glob": "^7.1.6", "highlight.js": "^10.4.1", "live-server": "^1.2.1", "mkdirp": "^1.0.4", "postcss": "^8.2.12", "postcss-calc": "^7.0.2", "postcss-copy": "^7.1.0", "postcss-css-variables": "^0.14.0", "postcss-inline": "^1.2.0", "postcss-inline-svg": "^4.1.0" } }], "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], @@ -226,6 +227,8 @@ "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + "@fontsource/roboto-condensed": ["@fontsource/roboto-condensed@5.2.8", "", {}, "sha512-M2OxXXGJQeif+LhxHjWiCEX3BHIYeZf64CcUTeh9AQFGp7DXo2UAXZiZUUOalVdOiuUtQSIi2CDHLDWFiyoECQ=="], + "@hypeup/babel": ["@hypeup/babel@workspace:../../sig/hypeup/packages/babel"], "@hypeup/client": ["@hypeup/client@workspace:../../sig/hypeup/packages/client"], @@ -610,7 +613,7 @@ "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], - "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], @@ -706,8 +709,6 @@ "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], - "known-css-properties": ["known-css-properties@0.37.0", "", {}, "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ=="], - "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], @@ -832,6 +833,10 @@ "pinkie-promise": ["pinkie-promise@2.0.1", "", { "dependencies": { "pinkie": "^2.0.0" } }, "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw=="], + "playwright": ["playwright@1.60.0", "", { "dependencies": { "playwright-core": "1.60.0" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA=="], + + "playwright-core": ["playwright-core@1.60.0", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA=="], + "posix-character-classes": ["posix-character-classes@0.1.1", "", {}, "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg=="], "postcss": ["postcss@8.5.14", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg=="], @@ -1070,6 +1075,8 @@ "base/define-property": ["define-property@1.0.0", "", { "dependencies": { "is-descriptor": "^1.0.0" } }, "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA=="], + "chokidar/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "class-utils/define-property": ["define-property@0.2.5", "", { "dependencies": { "is-descriptor": "^0.1.0" } }, "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA=="], "connect/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], @@ -1174,6 +1181,8 @@ "unset-value/has-value": ["has-value@0.3.1", "", { "dependencies": { "get-value": "^2.0.3", "has-values": "^0.1.4", "isobject": "^2.0.0" } }, "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q=="], + "vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], "class-utils/define-property/is-descriptor": ["is-descriptor@0.1.8", "", { "dependencies": { "is-accessor-descriptor": "^1.0.1", "is-data-descriptor": "^1.0.1" } }, "sha512-SceYGWXvdqlWa/OnQ5FQuV+NxvNmMRhMw/w9AHkH71hTzveND4BTYgvp16g+oITK47qbOl/3D0bl0iygehWAWQ=="], diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..5b38bc3 --- /dev/null +++ b/compose.yml @@ -0,0 +1,6 @@ +name: da.nfin.ch +services: + printer: + image: gotenberg/gotenberg:8 + ports: + - "3000:3000" diff --git a/cv.pdf b/cv.pdf new file mode 100644 index 0000000..8a65280 Binary files /dev/null and b/cv.pdf differ diff --git a/package.json b/package.json index dbe1664..7758486 100644 --- a/package.json +++ b/package.json @@ -2,13 +2,6 @@ "name": "da.nfin.ch", "private": true, "type": "module", - "devDependencies": { - "@types/bun": "^1.3.13", - "@typescript/native-preview": "beta", - "oxfmt": "^0.47.0", - "oxlint": "^1.62.0", - "hypeup": "workspace:*" - }, "license": "MIT", "author": { "name": "Sigitex", @@ -18,31 +11,42 @@ "type": "git", "url": "git+https://github.com/sigitex/da.nfin.ch.git" }, + "files": [ + "src" + ], "scripts": { "check": "tsgo --build", "test": "bun test --pass-with-no-tests --tsconfig-override tsconfig.test.json", "lint": "oxlint", "start": "hypeup generate --watch", - "build": "hypeup generate", - "publish:main": "rsync -avz --delete dist/ da.nfin.ch:~/da.nfin.ch", + "build": "bun run build:site && bun run build:cv", + "build:site": "hypeup generate", + "build:cv": "bun scripts/build-cv.ts", + "publish:site": "rsync -avz --delete dist/ da.nfin.ch:~/da.nfin.ch", "publish:errilaz": "rsync -avz --delete errilaz.org/ da.nfin.ch:~/errilaz.org", - "publish": "bun run publish:main && bun run publish:errilaz" - }, - "files": [ - "src" - ], - "workspaces": { - "packages": [ - "/sig/hypeup/packages/*" - ] + "publish": "bun run publish:site && bun run publish:errilaz" }, "dependencies": { "98se.css": "/err/98se.css", "@danielx/civet": "^0.11.7", + "@fontsource/roboto-condensed": "^5.2.8", "@hypeup/client": "workspace:*", "@hypeup/lexicon": "workspace:*", "@hypeup/runtime": "workspace:*", "marked": "^18.0.3", "marked-gfm-heading-id": "^4.1.4" + }, + "devDependencies": { + "@types/bun": "^1.3.13", + "@typescript/native-preview": "beta", + "oxfmt": "^0.47.0", + "oxlint": "^1.62.0", + "hypeup": "workspace:*", + "playwright": "^1.54.1" + }, + "workspaces": { + "packages": [ + "/sig/hypeup/packages/*" + ] } } diff --git a/scripts/build-cv.sh b/scripts/build-cv.sh new file mode 100755 index 0000000..722196e --- /dev/null +++ b/scripts/build-cv.sh @@ -0,0 +1,10 @@ +#!/bin/bash +mkdir -p dist +thorium-browser \ + --headless=new \ + --no-sandbox \ + --disable-gpu \ + --virtual-time-budget=5000 \ + --align-pdf-default-print-settings-with-html \ + --print-to-pdf="dist/cv.pdf" \ + "http://localhost:5173/cv" diff --git a/scripts/build-cv.ts b/scripts/build-cv.ts new file mode 100644 index 0000000..32d8cdf --- /dev/null +++ b/scripts/build-cv.ts @@ -0,0 +1,41 @@ +#!/usr/bin/env bun +import { mkdir } from "node:fs/promises" +import { dirname } from "node:path" +import { chromium } from "playwright" + +const url = "file:///finch/da.nfin.ch/dist/cv.html" +const output = "dist/cv.pdf" + +await mkdir(dirname(output), { recursive: true }) + +const executablePath = + process.env.CHROMIUM_PATH ?? + Bun.which("thorium-browser") ?? + Bun.which("chromium") ?? + Bun.which("chromium-browser") ?? + Bun.which("google-chrome") ?? + undefined + +const browser = await chromium.launch({ + executablePath, + headless: true, + args: ["--no-sandbox", "--disable-gpu"], +}) + +try { + const page = await browser.newPage() + + await page.goto(url, { + waitUntil: "networkidle", + timeout: 30_000, + }) + + await page.pdf({ + path: output, + printBackground: true, + displayHeaderFooter: false, + preferCSSPageSize: true, + }) +} finally { + await browser.close() +} diff --git a/src/routes/cv.html.ts b/src/routes/cv.html.ts new file mode 100644 index 0000000..6584b06 --- /dev/null +++ b/src/routes/cv.html.ts @@ -0,0 +1 @@ +export { default } from "ui/CV" diff --git a/src/ui/CV/CV.ts b/src/ui/CV/CV.ts new file mode 100644 index 0000000..655165a --- /dev/null +++ b/src/ui/CV/CV.ts @@ -0,0 +1,192 @@ +import Reset from "ui/Layout/Reset" +import Markdown from "ui/shared/Markdown" +import intro from "ui/Home/intro.md?raw" +import details from "ui/Home/details.md?raw" +import skills from "ui/Home/skills.md?raw" +import experience from "ui/Experience/experience.md?raw" +import Sections from "ui/Tech/Sections" +import roboto from "@fontsource/roboto-condensed?url" + +const Color = { + backdrop: "#f0f0f0", + edge: "#808080", +} as const + +export default function CV() { + return [ + doctype.html5, + html( + head( + link({ rel: "stylesheet", href: roboto }), + style( + Reset(), + rule.pdf( + display.flex, + width("50px"), + height("50px"), + borderRadius("12px"), + backgroundColor("#345c42"), + color.white, + position.fixed, + top("25px"), + right("25px"), + justifyContent.center, + alignItems.center, + fontWeight.bold, + textDecoration.none, + ), + $page( + prop("size", "auto"), + margin("0 0 0 0"), + padding("0.5in"), + ), + rule(body, [ + fontFamily("Roboto Condensed"), + fontSize("12pt"), + display.flex, + justifyContent.center, + backgroundColor(Color.backdrop), + ]), + $media("print", [ + rule(body, backgroundColor.white), + rule.noprint(display.none), + ]), + rule(main, [ + backgroundColor.white, + width("8.5in"), + display.flex, + justifyContent.center, + paddingBottom("32pt"), + ]), + rule(article, [width("7.0in")]), + rule(header, [ + marginTop("0.5in"), + display.flex, + justifyContent.spaceBetween, + alignItems.flexEnd, + paddingBottom("8pt"), + ]), + rule(h1, fontSize("28pt")), + rule.links( + fontSize("11pt"), + width("2.75in"), + display.flex, + justifyContent.spaceBetween, + alignItems.center, + rule(">div", [ + display.flex, + flexDirection.column, + gap("2pt"), + ]), + ), + rule.contact( + alignItems.flexEnd, + fontSize("12pt"), + ), + rule(section, [ + borderTop(`double 3px ${Color.edge}`), + paddingTop("16pt"), + paddingBottom("16pt"), + ]), + rule(h2, [marginBottom("12pt")]), + rule(p, [marginTop("12pt")]), + rule(ul, [marginTop("12pt")]), + rule(hr, [ + margin("12pt 0"), + borderColor("#f0f0f0"), + ]), + rule(".experience .content h2", [ + fontSize("14pt"), + ]), + rule.expertise( + pageBreakInside.avoid, + rule(ul, marginBottom("12pt")), + rule.blocks(columns(3)), + rule.block( + flex(1), + minWidth("3in"), + breakInside.avoid, + ), + ), + ), + ), + body( + main( + article( + Header(), + About(), + Skills(), + Experience(), + Expertise(), + ), + ), + a.pdf.noprint({ href: "cv.pdf" }, "PDF"), + ), + ), + ] +} + +function Header() { + return header( + h1("Dan Finch"), + div.links( + div.www( + a({ href: "https://da.nfin.ch" }, "Website"), + a( + { href: "https://www.sigitex.com" }, + "Open Source", + ), + a( + { + href: "https://www.linkedin.com/in/danfinch", + }, + "LinkedIn", + ), + ), + div.contact( + a( + { href: "mailto:danfinch@outlook.com" }, + "danfinch@outlook.com", + ), + a({ href: "tel:+436645047583" }, "+43 664 5047583"), + ), + ), + ) +} + +function About() { + return section.about( + h2("About Me"), + Markdown(intro), + Markdown(details), + ) +} + +function Skills() { + return section.skills(h2("Skills"), Markdown(skills)) +} + +function Experience() { + return section.experience( + h2("Experience"), + Markdown(experience), + ) +} + +function Expertise() { + return section.expertise( + h2("Expertise"), + div.blocks( + Skillset("Languages", Sections.languages), + Skillset("Platforms", Sections.platforms), + Skillset("Data", Sections.data), + Skillset("Frontend", Sections.frontend), + Skillset("Backend", Sections.backend), + Skillset("Concepts", Sections.concepts), + ), + ) +} + +function Skillset(name: string, content: string) { + return div.block(h3(name), Markdown(content)) +} diff --git a/src/ui/CV/index.ts b/src/ui/CV/index.ts new file mode 100644 index 0000000..b57d548 --- /dev/null +++ b/src/ui/CV/index.ts @@ -0,0 +1 @@ +export { default } from "./CV" \ No newline at end of file diff --git a/src/ui/Experience/Experience.ts b/src/ui/Experience/Experience.ts index 16ebc81..032fe65 100644 --- a/src/ui/Experience/Experience.ts +++ b/src/ui/Experience/Experience.ts @@ -14,7 +14,11 @@ export default function Experience() { { title }, div( padding("16px"), - Markdown(experience), + div.content( + h1("My Experience"), + hr(), + Markdown(experience), + ), ), ), ), diff --git a/src/ui/Experience/experience.md b/src/ui/Experience/experience.md index 7dd493c..1656738 100644 --- a/src/ui/Experience/experience.md +++ b/src/ui/Experience/experience.md @@ -1,7 +1,3 @@ -# My Experience - ---- - ## Dynatrace - Senior Software Engineer > July 2023 - Present (Graz, Austria - Hybrid) @@ -80,7 +76,7 @@ operations, testing, maintenance, and security. - Created complex e-commerce website featuring retail and affiliate marketing management. Owner of dozens of projects within the company. - Configured and managed production web servers including Microsoft IIS and LAMP stack. - Worked closely with DBA on data-driven applications powered by SQL Server 2008 and ADO.NET. -- Analyzed and selected supporting software and services, including early adoption of technologies such as ASP.NET MVC, jQuery, F#, Amazon Web Services, and distributed source control. +- Analyzed and selected supporting software and services, including early adoption of technologies such as ASP.NET MVC, jQuery, F#, AWS, and distributed source control. - Developed solutions using Service Oriented Architecture (SOA), WCF, and Windows services. Integrated software with various cloud data and web service providers, including AWS, Authorize.NET, and Zencoder. - Studied and implemented e-commerce best practices, web security standards, and PCI compliance. diff --git a/src/ui/Home/Home.ts b/src/ui/Home/Home.ts index 80d9042..13eb093 100644 --- a/src/ui/Home/Home.ts +++ b/src/ui/Home/Home.ts @@ -1,6 +1,9 @@ import Layout from "ui/Layout" import Markdown from "ui/shared/Markdown" -import about from "./about.md?raw" +import intro from "./intro.md?raw" +import details from "./details.md?raw" +import skills from "./skills.md?raw" +import interests from "./interests.md?raw" import Window from "ui/Layout/Window" const title = "Dan Finch - About Me" @@ -8,6 +11,20 @@ const title = "Dan Finch - About Me" export default function Home() { return Layout( { title, url: "/" }, - Window({ title }, div(padding("8px"), Markdown(about))), + Window( + { title }, + div.content(padding("8px"), [ + h1("About Me"), + Markdown(intro), + aside.fieldBorderDisabled( + img({ src: "/pfp.jpg", width: "200" }), + ), + Markdown(details), + h2("Skills"), + Markdown(skills), + h2("Interests"), + Markdown(interests), + ]), + ), ) } diff --git a/src/ui/Home/about.md b/src/ui/Home/about.md deleted file mode 100644 index f4427a9..0000000 --- a/src/ui/Home/about.md +++ /dev/null @@ -1,29 +0,0 @@ -# About Me - -I own software end-to-end: building or renewing stacks and evaluating the tools and techniques that will serve the product well. I assist my teams in creating frameworks, automation, standards, and guidance that clear the path to delivering good software and user experiences. I am comfortable working hands-on across code, architecture, UX, and product. - - - -My first principle is **user experience**. Software is interesting because of what it can do for people, and we all put up with mediocre software every day. This dissatisfaction drives me to set a high standard for our work. - -As a programmer, I've learned to make code **small, simple, and readable**. My aim is prolific and expeditious code output while maintaining quality. I make loosely-coupled components, understandable abstractions, and low-ceremony frameworks. - -As a teammate and leader, I am devoted to **friendliness** and **respect**. I am a good communicator, love sharing my knowledge, and learning from others. I've given talks at local user groups and mentored at work and in my free time. - -## Skills - -- Consciousness of mainstream & emerging technology and trends. -- Quickly vetting and adapting to new technologies - building and improving stacks. -- High standards for great user and developer experiences. -- Interacting directly and empathetically with stakeholders and end users. -- Managing work through all phases of a development cycle. -- Eliminating friction in processes through automation. -- Understanding all the areas of a codebase and recognizing where waste can be reduced. -- Coordinating large, complex upgrades and refactors. - -## Interests - -- Compilers, interpreters & parsers. -- Metaprogramming, language-oriented programming. -- Developer tools - hacking on build tools and bundlers. -- Automating self-hosted cloud tech. diff --git a/src/ui/Home/details.md b/src/ui/Home/details.md new file mode 100644 index 0000000..a6c1cbb --- /dev/null +++ b/src/ui/Home/details.md @@ -0,0 +1,5 @@ +My first principle is **user experience**. Software is interesting because of what it can do for people, and we all put up with mediocre software every day. This dissatisfaction drives me to set a high standard for our work. + +As a programmer, I've learned to make code **small, simple, and readable**. My aim is prolific and expeditious code output while maintaining quality. I make loosely-coupled components, understandable abstractions, and low-ceremony frameworks. + +As a teammate and leader, I am devoted to **friendliness** and **respect**. I am a good communicator, love sharing my knowledge, and learning from others. I've given talks at local user groups and mentored at work and in my free time. \ No newline at end of file diff --git a/src/ui/Home/interests.md b/src/ui/Home/interests.md new file mode 100644 index 0000000..48e4788 --- /dev/null +++ b/src/ui/Home/interests.md @@ -0,0 +1,4 @@ +- Compilers, interpreters & parsers. +- Metaprogramming, language-oriented programming. +- Developer tools - hacking on build tools and bundlers. +- Automating self-hosted cloud tech. \ No newline at end of file diff --git a/src/ui/Home/intro.md b/src/ui/Home/intro.md new file mode 100644 index 0000000..521eb75 --- /dev/null +++ b/src/ui/Home/intro.md @@ -0,0 +1 @@ +I own software end-to-end: building or renewing stacks and evaluating the tools and techniques that will serve the product well. I assist my teams in creating frameworks, automation, standards, and guidance that clear the path to delivering good software and user experiences. I am comfortable working hands-on across code, architecture, UX, and product. \ No newline at end of file diff --git a/src/ui/Home/skills.md b/src/ui/Home/skills.md new file mode 100644 index 0000000..4f83182 --- /dev/null +++ b/src/ui/Home/skills.md @@ -0,0 +1,8 @@ +- Consciousness of mainstream & emerging technology and trends. +- Quickly vetting and adapting to new technologies - building and improving stacks. +- High standards for great user and developer experiences. +- Interacting directly and empathetically with stakeholders and end users. +- Managing work through all phases of a development cycle. +- Eliminating friction in processes through automation. +- Understanding all the areas of a codebase and recognizing where waste can be reduced. +- Coordinating large, complex upgrades and refactors. \ No newline at end of file diff --git a/src/ui/Layout/Theme.ts b/src/ui/Layout/Theme.ts index d4dfc99..b2be678 100644 --- a/src/ui/Layout/Theme.ts +++ b/src/ui/Layout/Theme.ts @@ -17,16 +17,15 @@ export default function Theme() { display.flex, alignItems.center, ]), - rule("h1", fontSize("20px")), - rule("h2", fontSize("16px")), - rule("ul", paddingInlineStart("30px")), - rule( - ".markdown", + rule(h1, fontSize("20px")), + rule(h2, fontSize("16px")), + rule(ul, paddingInlineStart("30px")), + rule.content( textAlign.justify, fontFamily("Arial"), fontSize("14px"), - rule("h1", fontSize("28px")), - rule("h2", fontSize("20px")), + rule(h1, fontSize("28px")), + rule(h2, fontSize("20px")), rule(aside, float.right, marginLeft("20px"), diff --git a/src/ui/Tech/Sections.ts b/src/ui/Tech/Sections.ts new file mode 100644 index 0000000..58f7ec2 --- /dev/null +++ b/src/ui/Tech/Sections.ts @@ -0,0 +1,78 @@ +export default { + languages: ` +- TypeScript +- JavaScript +- C# +- F# +- SQL +`, + platforms: ` +- Node.js +- Deno +- Bun +- HTML + CSS +- Linux +- Electron +- Browser Extensions +- React Native +- .NET Core +- .NET Framework +`, + frontend: ` +- React +- Vite +- Zustand +- Immer +- Webpack +- Parcel +- Emotion +- Styled Components +- Mithril +- Redux +- React Router +- React Query +- Vanilla Extract +- CSS Modules +- SCSS, Stylus, Less +`, + backend: ` +- Express +- Koa +- ASP.NET +- Fastify +- Hapi +- NestJS +- Docker +- Cloudflare +- Firebase +- Supabase +- AWS +- GitHub Actions +`, + data: ` +- Postgres +- Redis +- Sqlite +- MongoDB +- MySQL +- ElasticSearch +- SQL Server +`, + concepts: ` +- Domain-driven design (DDD) +- TDD & BDD +- Unit testing +- E2E testing +- Object-Oriented Programming (OOP) +- Functional Programming (FP) +- SOLID Principles +- REST & Microservices +- CI/CD, Infrastructure as Code +- RDBMS +- NoSQL +- Agile & Scrum +- Unix philosophy +- Responsive design +- IoC & Dependency Injection +`, +} diff --git a/src/ui/Tech/Tech.ts b/src/ui/Tech/Tech.ts index 0ad1b02..f864394 100644 --- a/src/ui/Tech/Tech.ts +++ b/src/ui/Tech/Tech.ts @@ -1,6 +1,7 @@ import Layout from "ui/Layout" import Window from "ui/Layout/Window" import Markdown from "ui/shared/Markdown" +import Sections from "./Sections" const title = "Dan Finch - Tech" @@ -10,18 +11,22 @@ export default function Tech() { Window( { title, flat: true }, div( - display.flex, - flexWrap.wrap, - marginRight("8px"), - paddingBottom("16px"), - gap("8px"), - Skillset("Languages", languages), - Skillset("Platforms", platforms), - Skillset("Frontend", frontend), - Skillset("Backend", backend), - Skillset("Data", data), - Skillset("Cloud / Infra / DevOps", cloud), - Skillset("Methodologies & Concepts", concepts), + [ + display.flex, + flexWrap.wrap, + marginRight("8px"), + paddingBottom("16px"), + gap("8px"), + ], + Skillset("Languages", Sections.languages), + Skillset("Platforms", Sections.platforms), + Skillset("Frontend", Sections.frontend), + Skillset("Backend", Sections.backend), + Skillset("Data", Sections.data), + Skillset( + "Methodologies & Concepts", + Sections.concepts, + ), ), ), ) @@ -29,91 +34,13 @@ export default function Tech() { function Skillset(title: string, markdown: Content) { return div( - flex(1), - minWidth("250px"), - display.flex, - flexDirection.column, + [ + flex(1), + minWidth("250px"), + display.flex, + flexDirection.column, + ], h2(title), div.fieldBorder(flex(1), Markdown(markdown)), ) } - -const languages = ` -- ⭐ TypeScript -- ⭐ JavaScript -- ⭐ C# -- F# -- SQL -` - -const platforms = ` -- ⭐ Node.js / Deno / Bun -- ⭐ HTML + CSS -- ⭐ .NET & .NET Core -- Linux -- Electron -- Browser Extensions -- React Native -- Cloudflare Workers -` - -const frontend = ` -- ⭐ React -- ⭐ Vite -- ⭐ Zustand -- ⭐ Immer -- ⭐ Webpack -- ⭐ Parcel -- ⭐ Emotion / Styled Components -- ⭐ Mithril -- Redux -- React Router -- TanStack Query, React Query -- Vanilla Extract -- SCSS, Stylus, Less, CSS Modules -- Lodash, Immer, RxJS, Kefir -` - -const backend = ` -- ⭐ Express -- ⭐ Koa -- ⭐ ASP.NET -- Fastify -- Hapi -- Nest -` - -const data = ` -- ⭐ Postgres -- ⭐ Redis -- Sqlite -- MongoDB -- MySQL -- ElasticSearch -- SQL Server -` - -const cloud = ` -- Docker -- Cloudflare -- Firebase -- Supabase -- AWS -- GitHub Actions -` - -const concepts = ` -- Domain-driven design (DDD) -- TDD & BDD, unit testing, & E2E testing -- Object-Oriented Programming (OOP) -- Functional Programming (FP) -- SOLID Principles -- REST & Microservices -- CI/CD, Infrastructure as Code -- RDBMS -- NoSQL -- Agile & Scrum -- Unix philosophy -- Responsive design -- IoC & Dependency Injection -` diff --git a/src/ui/shared/Markdown.ts b/src/ui/shared/Markdown.ts index 645de60..d4ac340 100644 --- a/src/ui/shared/Markdown.ts +++ b/src/ui/shared/Markdown.ts @@ -2,7 +2,7 @@ import { Marked } from "marked" import { gfmHeadingId } from "marked-gfm-heading-id" export default function Markdown(md: string) { - return div.markdown(raw(marked.parse(md))) + return div.content(raw(marked.parse(md))) } const marked = new Marked({ gfm: true }) diff --git a/tsconfig.config.json b/tsconfig.config.json index f58033a..eed6a13 100644 --- a/tsconfig.config.json +++ b/tsconfig.config.json @@ -8,7 +8,8 @@ }, "include": [ "*.config.ts", - "*.config.cjs" + "*.config.cjs", + "scripts/*.ts" ], "files": [] }