Personal portfolio featuring 3D PCB models https://portfolio.db-serve.com/
  • Astro 43.5%
  • JavaScript 26%
  • TypeScript 19.9%
  • CSS 7.9%
  • HTML 2.3%
  • Other 0.4%
Find a file
Dragos Bajanica 8b1029ae82
All checks were successful
build-image / docker (push) Successful in 39s
Fix LFS build
2026-08-24 18:04:05 -04:00
.claude Reduced initial performance cost of model-viewer 2026-08-15 12:25:42 -04:00
.forgejo/workflows Fix LFS build 2026-08-24 18:04:05 -04:00
public Ported scripts to TypeScript 2026-08-15 14:32:44 -04:00
scripts Ported scripts to TypeScript 2026-08-15 14:32:44 -04:00
src Changed color palette 2026-08-18 16:02:27 -04:00
.dockerignore Redesign of website 2026-08-07 22:27:06 -04:00
.gitattributes Initial website 2026-07-19 11:55:40 -04:00
.gitignore Further layout fixes 2026-08-15 18:03:14 -04:00
astro.config.ts Rewrite and redesign with model-viewer 2026-08-13 21:00:46 -04:00
DESIGN.md Changed color palette 2026-08-18 16:02:27 -04:00
Dockerfile Complete rewrite to Astro with Tailwind 2026-08-04 14:28:41 -04:00
nginx.conf Fixed redirect 2026-08-13 21:26:15 -04:00
package-lock.json Ported scripts to TypeScript 2026-08-15 14:32:44 -04:00
package.json Ported scripts to TypeScript 2026-08-15 14:32:44 -04:00
PLAN.md Further layout fixes 2026-08-15 18:03:14 -04:00
PRODUCT.md Ported scripts to TypeScript 2026-08-15 14:32:44 -04:00
README.md Further layout fixes 2026-08-15 18:03:14 -04:00
resume.yaml Rewrite and redesign with model-viewer 2026-08-13 21:00:46 -04:00
tsconfig.json Rewrite and redesign with model-viewer 2026-08-13 21:00:46 -04:00

Portfolio

Dragos Bajanica's personal portfolio, built with Astro and Tailwind CSS. Statically generated. No client JavaScript runs for content, navigation, or SEO metadata.

Editing projects

Each project is a Markdown file in src/content/projects/, one file per project. The homepage grid and each project's detail page are both generated from this folder. Nothing else needs to change to add, edit, or remove one.

Adding a new project

  1. Create src/content/projects/<slug>.md. <slug> becomes the URL, /projects/<slug>/.
  2. Fill in the frontmatter (see field reference below) and write the long-form write-up as the Markdown body.
  3. If it has a photo, drop the original into src/assets/projects/ and point image at it with a relative path (e.g. ../../assets/projects/board.png). Astro resizes and re-encodes it to WebP at build time, so use the original file, not a pre-converted one.
  4. If it's a PCB, export and compress the .glb (see 3D models below), drop the compressed file into public/models/, and point glbPath at it.
  5. Set order to control where it sits in the project grid.

Frontmatter fields

  • title, summary, category (hardware or software), tags, order are required.
  • repoUrl and liveUrl are optional links.
  • image is an optional preview photo (see step 3 above).
  • glbPath is an optional path to a .glb model under public/models/, rendered with <model-viewer> on the project's detail page (and on the homepage hero, for UART Board).
  • specs and highlights are optional arrays feeding the detail page's spec sheet and bullet list.

Your own photo for the About section: src/assets/portrait.png. It needs to be a true-color image, not a palette/indexed PNG, or it'll show visible banding once rendered at full size.

3D models

.glb files are exported from KiCad with kicad-cli pcb export glb, not parsed in the browser. The raw export needs cleanup before it's usable: kicad-cli leaves every part material at glTF's raw defaults (metalness=1.0, roughness=1.0), which renders flat no matter how the scene is lit; ships silkscreen as translucent, so it fights the copper traces beneath it at glancing angles; leaves hundreds of same-material primitives per part unmerged; and, on boards that aren't green, gets the soldermask, copper, and substrate colors wrong.

scripts/process-pcb-model.ts fixes all of that in one pass:

node scripts/process-pcb-model.ts board_raw.glb public/models/board.glb

It patches metalness and roughness from each part's own flat color, fixes the silkscreen render order, merges same-material primitives into fewer draw calls, corrects the board's color scheme (detected from the soldermask automatically, or pass --scheme=green / --scheme=blue if detection isn't confident), and compresses the result with Draco. Commit the output to public/models/, not the raw export.

It won't recolor individual parts, like turning one connector black. That's a one-off judgment call, done by hand on top of the script's output when a board needs it.

scripts/render-poster.ts renders the static poster image each model shows before the interactive viewer takes over. It drives the real <model-viewer> in a headless browser (scripts/poster-harness.html is the page it loads), using the site's actual lighting and camera, so the poster matches the live model instead of jump-cutting into it on load. Run the dev server first, then:

node scripts/render-poster.ts /models/board.glb src/assets/projects/board-poster.png 798 760

The width and height need to match the real pixel size of wherever the poster is shown. <model-viewer>'s camera distance depends on the container's aspect ratio, so a poster rendered at the wrong size ends up looking like a different zoom level than the live model that replaces it.

Resume

There's no resume page. The header and hero's Resume links open resume.pdf directly. resume.yaml (RenderCV-style YAML) is a source of truth to sync against by hand whenever the PDF changes, but nothing on the site parses or displays it.

Commands

npm install               # install dependencies
npm run dev               # start the dev server
npm run build             # type-check and build to dist/
npm run check             # type-check the Astro project
npm run typecheck         # type-check scripts/ on its own
npm run preview           # preview the production build locally
npm run process-pcb-model # clean up a raw kicad-cli glb export
npm run render-poster     # render a model's static poster image

Deploying

Dockerfile builds the site and serves it from nginx-unprivileged. nginx.conf sets cache headers, gzip, and security headers (CSP, HSTS, X-Frame-Options, and so on). .forgejo/workflows/build.yml builds and pushes the image on every push to main.

Docs

PRODUCT.md covers the product brief: audience, goals, and content constraints. DESIGN.md covers the visual design system: colors, type, layout, and component rules.