Skip to content
Merged

V3 #3

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
a5bad66
Add link and asset checker with a weekly scan
JAlbertCode Sep 5, 2026
117ec89
Portfolio V3: route by what the visitor came for
JAlbertCode Sep 5, 2026
3ec3cfc
Fix two stream links that pointed at each other's videos
JAlbertCode Sep 5, 2026
b2a280e
Add /lab design directions and the connect surface
JAlbertCode Sep 5, 2026
d88425b
Add the Quiet direction: fast and plain, done properly
JAlbertCode Sep 5, 2026
b6d1315
Add V4, and strip filler everywhere
JAlbertCode Sep 5, 2026
153bbfb
Cut the mobile banner down to what is load-bearing
JAlbertCode Sep 5, 2026
3b67dbf
Reduce the banner to a sentence, a button, and four marks
JAlbertCode Sep 5, 2026
1779ca6
Drop the dead lane-card rules
JAlbertCode Sep 5, 2026
b7384a8
Apply the design to the actual site and delete the lab
JAlbertCode Sep 5, 2026
b7d1f71
Collapse the site to one page
JAlbertCode Sep 5, 2026
b182a21
Drop solutions engineering from the site copy
JAlbertCode Sep 6, 2026
7612f37
Fix the filtered state, and a border regression I introduced
JAlbertCode Sep 6, 2026
2193926
Correct a fabricated GDC talk, and stop centring the site on devrel
JAlbertCode Sep 6, 2026
a6069fb
One treatment per thing, and route print work to Layerworks
JAlbertCode Sep 6, 2026
06b0d56
One button, then links
JAlbertCode Sep 6, 2026
db86023
Drop the vCard, lead the connect row with Telegram/X/LinkedIn
JAlbertCode Sep 6, 2026
b15e055
Give the covers one system, and add the Midnight work
JAlbertCode Sep 6, 2026
562efd8
One work list, two sizes, and a preview beside it
JAlbertCode Sep 6, 2026
54826fa
Put something on the first screen
JAlbertCode Sep 6, 2026
c14f252
Back to cards, one treatment for all 86
JAlbertCode Sep 6, 2026
b33d150
Jay, Twitter, and summaries for all 21 hangouts
JAlbertCode Sep 6, 2026
28b01b2
Exact dates where the source has one
JAlbertCode Sep 6, 2026
a7514c2
Stop one bad file taking the whole cover build down
JAlbertCode Sep 6, 2026
eed33c5
Covers for the last two, two more articles, and routing above the work
JAlbertCode Sep 6, 2026
ceca15c
Add the Hacktoberfest contributor guide
JAlbertCode Sep 6, 2026
4f1c624
Take the pricing off the services page, and let small covers fill the…
JAlbertCode Sep 6, 2026
e5a7f3b
Order by last change, uniform rails, the whole catalogue on the page
JAlbertCode Sep 6, 2026
917e0f9
Fix the patcher that put covers on the wrong entries
JAlbertCode Sep 6, 2026
5f775b6
Covers for the last two articles, from the docs repo
JAlbertCode Sep 6, 2026
7004c05
Real UI screenshots for the three that have one
JAlbertCode Sep 7, 2026
2ebffda
The whole work history on About, not three roles
JAlbertCode Sep 7, 2026
a4e5ef6
Finalized home page
JAlbertCode Sep 7, 2026
79ee4cb
Updating calendar booking
JAlbertCode Sep 7, 2026
493efd7
Cleaned up code
JAlbertCode Sep 7, 2026
637e59c
Cleaned code
JAlbertCode Sep 7, 2026
4bce4e1
Additional cleaning
JAlbertCode Sep 7, 2026
999e6db
Updated checks
JAlbertCode Sep 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions .github/workflows/link-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
name: Link check

# The portfolio points at a lot of other people's platforms. Articles get
# unpublished, videos get pulled, side-project deployments lapse. This walks
# every link and asset on a schedule and files an issue when something rots.

on:
schedule:
# 09:00 UTC every Monday.
- cron: '0 9 * * 1'
workflow_dispatch:
pull_request:
paths:
- 'portfolio/lib/**'
- 'portfolio/app/**'
- 'portfolio/components/**'
- 'portfolio/scripts/check-links.mjs'

permissions:
contents: read
issues: write

jobs:
check:
runs-on: ubuntu-latest
defaults:
run:
working-directory: portfolio

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 22

# No install step: the checker deliberately has zero dependencies so it
# runs from a bare checkout.
- name: Check links and assets
id: check
continue-on-error: true
run: node scripts/check-links.mjs --markdown link-report.md --json link-report.json

- name: Upload report
uses: actions/upload-artifact@v4
with:
name: link-report
path: |
portfolio/link-report.md
portfolio/link-report.json
retention-days: 90

- name: Summarise on the run page
run: cat link-report.md >> "$GITHUB_STEP_SUMMARY"

# One rolling issue rather than a new one every week. If it is already
# open we replace the body so it always shows the current state; if the
# last run fixed everything we close it.
- name: File or update the issue
if: github.event_name != 'pull_request'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs')
const report = fs.readFileSync('portfolio/link-report.md', 'utf8')
const data = JSON.parse(fs.readFileSync('portfolio/link-report.json', 'utf8'))
const broken = data.results.filter(r => r.status === 'broken')
const TITLE = 'Broken links on the portfolio'

const { data: open } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: 'link-rot',
})
const existing = open.find(i => i.title === TITLE)

const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`
const body = `${report}\n\n---\n[Full run](${runUrl})`

if (broken.length === 0) {
if (existing) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: existing.number,
body: `All links resolve again as of ${new Date().toISOString().slice(0, 10)}. Closing.`,
})
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: existing.number,
state: 'closed',
})
}
return
}

if (existing) {
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: existing.number,
body,
})
} else {
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: TITLE,
labels: ['link-rot'],
body,
})
}

# Only the scheduled and manual runs can fail.
#
# A pull request touching one CSS file was failing because a video on
# someone else's platform had been taken down. That is real information,
# and it is already captured: the weekly run files a rolling issue and
# closes it when the links resolve again. What it is not is a reason to
# block a change, because nobody reviewing that PR can fix YouTube.
#
# The check still runs on every pull request and still writes its report
# to the run summary, so a link this branch actually broke is visible
# before merge. It just does not gate the merge.
- name: Fail if anything is broken
if: github.event_name != 'pull_request' && steps.check.outcome == 'failure'
run: exit 1
Binary file added Claude outputs/arcade-loud-desktop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/arcade-loud-mobile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/arcade-restrained-desktop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/c-home.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/c-mobile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/c-services.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/d-home.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/d-mobile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/detail-full.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/final-desktop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/final-mobile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/first-screen-mobile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/first-screen.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/hack-buenos-aires-shortlist.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/home-light2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/home.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/landing.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
118 changes: 118 additions & 0 deletions Claude outputs/link-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
name: Link check

# The portfolio points at a lot of other people's platforms. Articles get
# unpublished, videos get pulled, side-project deployments lapse. This walks
# every link and asset on a schedule and files an issue when something rots.

on:
schedule:
# 09:00 UTC every Monday.
- cron: '0 9 * * 1'
workflow_dispatch:
pull_request:
paths:
- 'portfolio/lib/**'
- 'portfolio/app/**'
- 'portfolio/components/**'
- 'portfolio/scripts/check-links.mjs'

permissions:
contents: read
issues: write

jobs:
check:
runs-on: ubuntu-latest
defaults:
run:
working-directory: portfolio

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 22

# No install step: the checker deliberately has zero dependencies so it
# runs from a bare checkout.
- name: Check links and assets
id: check
continue-on-error: true
run: node scripts/check-links.mjs --markdown link-report.md --json link-report.json

- name: Upload report
uses: actions/upload-artifact@v4
with:
name: link-report
path: |
portfolio/link-report.md
portfolio/link-report.json
retention-days: 90

- name: Summarise on the run page
run: cat link-report.md >> "$GITHUB_STEP_SUMMARY"

# One rolling issue rather than a new one every week. If it is already
# open we replace the body so it always shows the current state; if the
# last run fixed everything we close it.
- name: File or update the issue
if: github.event_name != 'pull_request'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs')
const report = fs.readFileSync('portfolio/link-report.md', 'utf8')
const data = JSON.parse(fs.readFileSync('portfolio/link-report.json', 'utf8'))
const broken = data.results.filter(r => r.status === 'broken')
const TITLE = 'Broken links on the portfolio'

const { data: open } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: 'link-rot',
})
const existing = open.find(i => i.title === TITLE)

const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`
const body = `${report}\n\n---\n[Full run](${runUrl})`

if (broken.length === 0) {
if (existing) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: existing.number,
body: `All links resolve again as of ${new Date().toISOString().slice(0, 10)}. Closing.`,
})
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: existing.number,
state: 'closed',
})
}
return
}

if (existing) {
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: existing.number,
body,
})
} else {
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: TITLE,
labels: ['link-rot'],
body,
})
}

- name: Fail if anything is broken
if: steps.check.outcome == 'failure'
run: exit 1
Binary file added Claude outputs/n-about.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/n-home.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/n-mobile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/n-services.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/portfolio-home.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/portfolio-work-list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/quiet-dark-desktop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/quiet-dark-mobile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Claude outputs/quiet-hover.png
Binary file added Claude outputs/quiet-light-desktop.png
Binary file added Claude outputs/services-top.png
Binary file added Claude outputs/speaking-top.png
Binary file added Claude outputs/terminal-loud-desktop.png
Binary file added Claude outputs/terminal-loud-mobile.png
Binary file added Claude outputs/terminal-restrained-desktop.png
Binary file added Claude outputs/v4-a.png
Binary file added Claude outputs/v4-b.png
Binary file added Claude outputs/v4-c.png
Binary file added Claude outputs/v4-d.png
Binary file added Claude outputs/v4-mobile.png
Binary file added Claude outputs/work-filtered.png
Binary file added Claude outputs/work-hover.png
Binary file added Claude outputs/work-mobile.png
Binary file added Claude outputs/work-top.png
Binary file added Claude outputs/workshop-loud-desktop.png
Binary file added Claude outputs/workshop-restrained-desktop.png
Binary file added Claude outputs/workshop-restrained-mobile.png
Binary file added Claude outputs/zine-loud-desktop.png
Binary file added Claude outputs/zine-loud-mobile.png
Binary file added Claude outputs/zine-restrained-desktop.png
39 changes: 11 additions & 28 deletions portfolio/.gitignore
Original file line number Diff line number Diff line change
@@ -1,38 +1,21 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
node_modules/
.next/
out/
build/
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

# In progress
app/art-bot
# link checker output
link-report.md
link-report.json
scripts/.refs.json

# V2 files V3 replaced. Safe to delete this folder.
_v2-removed/
Loading
Loading