Source for wik-wav.github.io: a static, trilingual (PL/EN/ZH) portfolio generated from JSON content and deployed to GitHub Pages.
15 routes, 11 project views, 66 works — 59 of them in the all-work gallery.
Po polsku. Strona portfolio Wiktora Sielaszuka publikowana przez GitHub Pages. Witryna jest trójjęzyczna: polski, angielski i chiński (uproszczony); każda trasa działa pod jednym adresem, a języki przełącza przełącznik w nagłówku. Treść jest oddzielona od kodu: źródłem prawdy są pliki w content/, a HTML tras, dane JS, metadane SEO, mapa witryny i manifest tras są generowane. Do edycji służy Portfolio Studio (npm run studio); publikacja odbywa się przez commit i push na main, a następnie przez GitHub Actions. Dalsza dokumentacja jest po angielsku.
- Node.js 22 or newer.
- ImageMagick 7, as
magickonPATHor pointed at byPORTFOLIO_MAGICK. The media pipeline andnpm testneed it.
git clone https://github.com/wik-wav/wik-wav.github.io.git
cd wik-wav.github.io
npm ci
npm run studionpm run studio starts two servers, both on loopback:
| URL | What |
|---|---|
http://127.0.0.1:4310 |
Portfolio Studio editor |
http://127.0.0.1:4177 |
Vite preview of the generated site |
Override the ports with PORTFOLIO_STUDIO_PORT and PORTFOLIO_PREVIEW_PORT.
| Command | Does |
|---|---|
npm run studio |
Editor plus live preview |
npm run dev |
Regenerate, then start the preview only |
npm run generate |
Rewrite all generated files |
npm run generate:check |
Fail if generated files are stale |
npm run validate:content |
Strict publish-time validation |
npm run media:dimensions |
Rewrite assets/js/media-dimensions.js |
npm test |
Validation, drift check, dimensions, build, unit tests |
npm run build |
Regenerate, then emit dist/ |
npm run preview |
Serve the built dist/ |
npm run migrate:content is the one-off import from the pre-Studio assets/js/data.js format; it is not part of normal work.
content/ site.json, projects/, collections/, works/, updates/ — source of truth
studio/ local editor: server and browser UI
scripts/ generator, validator, media pipeline
assets/ css/, fonts/, media/, js/ (data.js and site-data.js are generated)
public/ passthrough: favicons, robots.txt, site.webmanifest, sitemap.xml
tests/ node:test suites
generated/ site-manifest.json
Generated by npm run generate; do not edit by hand:
- every route
index.html assets/js/data.jsandassets/js/site-data.jsseo-metadata.tsgenerated/site-manifest.jsonpublic/sitemap.xml
npm run generate:check fails when any of these drift from content/.
content/ is authoritative. A work carries trilingual copy (titlePL/titleEN/titleZH, summary, alt, caption), project, medium, year, types, tags, and optional cover, hero, and thumbnail media.
Every localized value is a …PL/…EN/…ZH sibling triple. Polish and English are required; Chinese is optional and falls back to English when absent, so a record created in Studio still renders before its Chinese copy exists. Studio itself edits Polish and English only — Chinese lives in the JSON and can be filled in by hand.
Fields that change behaviour:
collections— puts a work in a collection without duplicating the record.galleryVisible/detailOnly— keep a work out of the all-work gallery while it stays in its project narrative.projectPageVisible: false— hides a work from its project page only.projectGroup— merges consecutive works into one semantic block without changing gallery presence.detailMediaSize—compact,standard,large, orfull; a work-level value overrides the project default.disclosure—ai-generatedorai-elements, see Disclosure.
Sequence images open a shared viewer with zoom, pan, and an optional details panel; video stays a responsive 16:9 embed.
15 routes: 4 top-level pages and 11 project views. The 8 projects and 3 collections share one route type.
index.html— profileportfolio/index.html— filterable galleryprojects/index.html— project indexactivity/index.html— activity logprojects/latentne/index.htmlprojects/para-analog-on/index.htmlprojects/gaijin-no-mittsu-no-kuusou/index.htmlprojects/lem/index.htmlprojects/lozenge-t/index.htmlprojects/book-cover/index.htmlprojects/windows-zine/index.htmlprojects/cover-art/index.htmlprojects/small-design-projects/index.htmlprojects/character-art/index.htmlprojects/dissonance-perspective/index.html
portfolio/ and projects/ accept size, per, and page, and projects also accepts view=visual|list. A valid query string wins over the stored per-route preference. Explicit page navigation pushes history, while filter, size, and per-page changes replace the current entry. The gallery preserves repeated filter parameters, and collection=<id> narrows it to one collection.
The editor writes content/ and never touches Git — no commit, no push.
It handles:
- creating, duplicating, reordering, and archiving projects and works, by drag, buttons, or an explicit position
- activity records built from ordered text, image, audio/video, and link blocks
- PL/EN copy, captions, alt text, tags, medium, year, SEO, and sequence order
- independent cover, hero, and thumbnail media, with
cover/containfit, focal point, page size, and a light or dark background for transparent works - image import: the original goes to the masters store, the repository gets an optimised WebP
- structured YouTube, Vimeo, SoundCloud, and Bandcamp embeds only, with audio players in compact, standard, or expanded size
Saving a record validates the whole content graph, writes only the files that changed, and refreshes the preview. Drafts stay out of the public build without blocking ready content.
Archiving precedes deletion. An archived work keeps its JSON and its permanently reserved URL, and source media is never deleted; the JSON can be removed permanently by typing its exact ID. Build for publishing runs the production build and reports Build ready · Git unchanged.
Site images are WebP derivatives; source originals are not committed.
The recipe: auto-orient, sRGB, long edge capped at 2400 px without upscaling, metadata stripped, WebP quality 82 / method 6, transparency preserved.
Exceptions:
assets/media/lem/caps at 2000 px, and its three-form hero and thumbnail composites use quality 88.assets/media/book-cover/keeps the source dimensions.- The bachelor film is an approved YouTube embed on
youtube-nocookie.com; no video files are committed.
npm run media:dimensions rebuilds assets/js/media-dimensions.js from the images on disk. Run it after adding or replacing media and commit the result — an image without recorded dimensions is an error.
disclosure belongs to a media asset and is rendered centrally by makeMedia(). ai-generated marks a generated visualisation; ai-elements marks work containing the named generated elements. It applies to the book mockup, the zine mockups, and the zine pages with ASCII art — not to the voice-model icon, character art, or video stills. This is a provenance convention for this portfolio, not legal certification.
The canonical origin is https://wik-wav.github.io, fixed in content/site.json and read-only in Studio. Metadata for all 15 routes is generated into seo-metadata.ts at build time.
Every production route emits one absolute canonical URL, a matching og:url, rel="author", rel="me" links to the configured profiles, and a JSON-LD graph joining Person, WebSite, and the page. Canonicals use a trailing slash and never carry filter, pagination, or viewer parameters. public/sitemap.xml comes from the same route manifest and is regression-tested against those canonicals.
The document is Polish on first paint; the PL/EN/ZH switch updates title, description, and the supported metadata. All three languages share one URL, so Chinese and English are client-side only; indexable per-language routes (/en/, /zh/) do not exist yet.
These signals make the original unambiguous to search engines. They do not prevent anyone from copying the public HTML or images.
.github/workflows/deploy.yml runs on every push to main and on manual dispatch: install ImageMagick, npm ci, npm test, then upload dist/ and deploy to GitHub Pages. A local build publishes nothing.
public/og-social.pngis an AI-generated portfolio preview visualisation and keeps its visible disclosure.studio-preview/holds draft previews, is gitignored, and is never published.- Chinese copy is set in 寒蝉锦书宋 (Chill Jinshu Song), a rounded Song-style face based on Source Han Serif. It ships as a subset covering only the glyphs this site uses (190 KB regular, 199 KB bold) in
assets/fonts/, under the SIL Open Font License 1.1 (assets/fonts/ChillJinshuSong-OFL.txt). It leads the font stacks only underhtml[lang="zh"], so Polish and English pages never download it. Glyphs introduced by later Chinese copy fall back to the system font until the subset is rebuilt. - The header language control is a native
<select>, so it needs no JavaScript to open and stays usable on touch devices.