Wonders of Work Review hub

Open changes waiting for a look, and what shipped recently. Anyone can open a preview below and leave feedback on GitLab.

Open for review

MR !66 by Jurjen de Vries ·

Fix

align list markers with item text

What this changes, and why

Fixes a vertical gap between the list marker (arrow bullet / number) and the item text in .prose-text lists. Loose-list items render their content inside <p>. Because .prose-text lists are CSS grid containers, each <li> establishes its own block formatting context, so the paragraph's prose margin-top no longer collapses and pushes the text below the marker.

Fix: zero margin-top on the first <p> and margin-bottom on the last <p> inside list items (.prose-text li > p).

Spotted on the Nostr event preview (workshop event), but it affects every .prose-text surface.

Also documents an existing implicit convention in AGENTS.md: merge request descriptions and test plans are written in English (the MR template already is).

What this affects

.prose-text li > p touches every .prose-text surface that renders a loose list (blank line between items, producing <li><p>…</p></li>):

  • Nostr event preview and story preview (render-markdown.js)
  • Strapi rich text via Markdown.astro: RichText, FAQs, Features, StepsGrid,
  • CardGrid, Spotlight, People, Categories, QuoteGrid, Newsletter, AudioPlayer, VideoGrid, ContactInfo

  • Hero.astro (inline marked)
  • Both bulleted (ul) and numbered (ol) lists

Not affected: .arrow-list standalone (RichText "chapters" nav — different class), and tight lists (no <p> inside <li>, selector does not match). No security-sensitive areas touched.

How to try it

On the preview link, open content that contains a bulleted or numbered list with blank lines between items, and confirm the marker now lines up with the first line of text:

  • [x] Nostr event preview (the original bug) — arrow lists line up
  • [x] Nostr story preview
  • [x] Strapi RichText block: ul and ol lists
  • [x] Numbered list (ol): number circle aligns with text
  • [x] Strapi blocks with markdown that may contain lists: FAQs, Features,
  • StepsGrid, CardGrid, Spotlight, People, Categories, QuoteGrid, Newsletter, AudioPlayer, VideoGrid, ContactInfo, Hero

  • [x] Tight lists (no blank lines between items): unchanged
  • [x] Multi-paragraph list items: spacing between paragraphs preserved
  • (only first/last margin removed)

Before you ask for review
  • [x] Branch named feat/…, fix/…, chore/…, docs/… or hotfix/…
  • [x] Commit messages follow Conventional Commits
  • [x] pnpm format has been run and the checks are green
  • [x] Tried it on the preview link
  • [x] Rebased on the latest main

Updates · newest first

  • preview ↗ Merge branch 'main' into fix/prose-list-item-paragraph-margin by ih-abir ·
  • preview ↗ docs(agents): require English for merge request descriptions by Jurjen de Vries ·
  • no preview fix(prose): remove margin gap between list marker and item text by Jurjen de Vries ·

Recently shipped to production (main) wondersofwork.nl 🎉

  • Other
    Feature/dynamic loader
    What this changes, and why

    Replaces all spinning SVG loaders with skeleton cards that match the shape of real story, event, and people cards. Loading.astro gains a variant prop ("story" | "event" | "people") with a count. The grids fill the right space from first paint, eliminating layout shift when content arrives.

    Also adds an empty state to the profile page ("No stories or events found") for profiles with no content.

    What this affects
    • src/components/Loading.astro — new skeleton variants with shimmer; default spinner unchanged
    • src/components/blocks/CardGrid.astro — stories/events grid uses skeleton cards; removed grid.innerHTML = "" that caused a blank flash on reload
    • src/components/blocks/People.astro — people grid uses skeleton cards
    • src/pages/[...locale]/event-preview.astro — RSVP "Going" grid pre-renders people skeletons; removed JS buildAttendeeLoader spinner
    • src/pages/[...locale]/profile-preview.astro — content area shows event skeletons while loading + empty state when nothing is found
    • public/scripts/card-generator.js, profile-card-generator.js — clearGrid now preserves .skeleton-placeholder elements until the first real card replaces them
    How to try it

    1. Open a page with stories, events, or a people section (eg: /en/stories/ , /en/calendar/ , /en/wonderful-people/). 2. Reload — you should see shimmer skeleton cards immediately instead of a spinner. 3. Watch cards fade in and replace the skeletons without any layout jump. 4. On an event page, check the "The people you'll meet" section loads skeletons first. 5. On a profile with no content, confirm the empty state appears instead of a blank area.

  • MR !67 by Jurjen de Vries ·
    Feature
    surface 👍-approved stories/events regardless of follow list
    What this changes, and why

    Expands how the stories/events overviews (and homepage blocks) are curated, in two steps.

    1. Thumbs-up curation. A 👍 (kind 7) reaction from the Wonders of Work account now surfaces an individual story (30023) or calendar event (31923) in the overviews even when its author is not on the curation list. Routed through nostr-queue (not a raw directFetch), because a raw concurrent REQ during initial load was answered CLOSED by relays that cap concurrent subscriptions, so approved content silently never rendered.

    2. Correct base curation source. The overviews were gated on the account's kind:3 follow list. The intended source is the NIP-51 kind:30000 follow set titled "Approved" (d-tag HmLpoIl0DhQjBGkAGXeTr, 22 members). Switched fetchDefaultFollowList to that set, and decoupled its cache (window.approvedFollowSetCache + localStorage key approvedFollowSetCache) from people-loader's window.defaultFollowListCache (the separate "Wonderful People" set used only by the people page), which the two overviews were sharing and could clobber.

    Net rule for an item to appear: in the Approved set OR has a 👍 from the account. Author-scoped detail/profile pages are untouched (they already show everything by that author).

    What this affects
    • public/scripts/content-loader.js — curation source, approval layer, queue routing, cache key.
    • src/components/blocks/CardGrid.astro, src/pages/[...locale]/profile-preview.astro — cache-bust version query only.
    • Surfaces: /calendar/, /stories/, homepage story/event blocks (all locales).
    • Not security-sensitive (no profile, key export, or OpenBunker code). People page ("Wonderful People") deliberately left unchanged.
    How to try it

    On the preview link, open /en/calendar/ and /en/stories/ (and the NL variants):

    • The base list reflects the Approved kind:30000 set (22 members), not the old 49-member kind:3 list.
    • An event whose author is in the Approved set shows (e.g. *recharging breathwork*, *proeverij leadership*).
    • An item with only a 👍 from the account, whose author is not in the Approved set, still shows (e.g. *rise-to-get-there*).
    • Profile pages are unchanged.

    Verified each step on the Cloudflare preview with a headless browser; no console errors.

    Before you ask for review
    • [x] Branch named feat/…
    • [x] Commit messages follow Conventional Commits
    • [x] pnpm format has been run and the checks are green
    • [x] Tried it on the preview link
    • [x] Rebased on the latest main
  • MR !65 by Jurjen de Vries ·
    CI
    make the MR description check blocking
    What this changes, and why

    Flips the mr-description CI job from a non-blocking warning to blocking (exit 0 to exit 1). The team has the workflow in place, so a missing section should now fail the pipeline instead of only warning.

    What this affects

    .gitlab-ci.yml (the mr-description job) and the matching wording in CONTRIBUTING.md. No application code. Only affects MR pipelines (the job runs on merge_request_event only).

    How to try it

    This MR description has all three required sections, so the mr-description job passes here. An MR missing any of them now fails that job (and the pipeline) until added.

  • MR !62 by Jurjen de Vries ·
    Docs
    formalise MR description expectations + CI check
    What this changes, and why

    Defines the expectation that every merge request includes a clear description, what it touches, and a test plan — and aligns the template, CONTRIBUTING and CI with that expectation, so the review hub at review.wondersofwork.nl becomes genuinely useful for non-technical and AI-assisted reviewers.

    • Adds a "What this affects" section to the merge request template, between "What this changes, and why" and "How to try it".
    • CONTRIBUTING.md gets a new "Writing a good MR description" section with the three expected fields (and a TOC entry).
    • CI gets a new mr-description job that warns when any of those sections is missing, non-blocking for now.
    What this affects
    • .gitlab/merge_request_templates/Default.md — adds one section.
    • CONTRIBUTING.md — new H2 section + TOC entry, no other changes.
    • .gitlab-ci.yml — one new job (mr-description), runs only on merge_request_event pipelines, currently exits 0 (warning mode). Flip the marked exit 0 to exit 1 when the team is ready to enforce.

    Nothing that touches application behaviour, production content or security-sensitive code.

    How to try it

    On this very MR: open the pipeline and find the mr-description job — it should print "✓ MR description has all required sections." (because this description has them). To see the warning case, open a quick test MR with a minimal description; the job should list the missing sections without failing the pipeline.

  • MR !64 by Jurjen de Vries ·
    Chore
    drop duplicate astro build job
    What this changes, and why

    Removes the build job from .gitlab-ci.yml. Cloudflare Pages already runs the full astro build on every MR (the "Cloudflare Pages" external status) and gates the merge, so running it again in GitLab CI was redundant and ~3x slower.

    What this affects

    .gitlab-ci.yml only. CI keeps format, typecheck, test, commitlint and mr-description — the checks Cloudflare does not cover. No application code touched. Production build path is unchanged (Cloudflare).

    How to try it

    On this MR: the pipeline should no longer show a build job, the "Cloudflare Pages" external status still runs, and the MR stays mergeable on green.

  • MR !63 by Jurjen de Vries ·
    Other
    Fix RSVP attendee profile rendering

    <!-- See CONTRIBUTING.md if anything here is unclear. -->

    What this changes, and why

    Fixes the event detail RSVP attendee list so profile names and photos render from fetched Nostr kind:0 profile events instead of getting stuck as placeholder attendee cards.

    The HAR for the Write Club event showed that attendee profile events could arrive while the attendee cards were still being built. The old code patched DOM nodes that did not exist yet, then the initial card render wrote Anonymous placeholders afterwards. This change renders attendee cards only for profiles that already have displayable profile data, shows the existing loader while RSVP/profile data is still actively loading, fetches missing attendee profiles, and re-renders the attendee grid as data arrives.

    The section heading is now visible as soon as the RSVP UI initializes. Counts are based on displayable attendee cards instead of raw RSVP events, so incomplete anonymous profiles do not inflate The people you will meet. The loader occupies the next card slot and is removed once the RSVP/profile fetch window is finished.

    How to try it

    Open the MR preview and visit:

    /en/event-preview/?d=event:write-club-mpmmxhfu576r1q22144q561o

    On a cold load, check the section The people you will meet. The heading should be visible before all relay data is finished. Attendee cards whose profile name is already cached can render immediately; the count should increase only for displayable attendee cards. Missing or incomplete profiles should show the loader in the next card position while loading is active, without showing temporary Anonymous cards, and the loader should disappear after the profile fetch completes or times out.

    Before you ask for review
    • [x] Branch named feat/…, fix/…, chore/…, docs/… or hotfix/…
    • [x] Commit messages follow Conventional Commits
    • [x] pnpm format has been run and the checks are green
    • [x] Tried it on the preview link
    • [x] Rebased on the latest main
  • MR !61 by Jurjen de Vries ·
    Chore
    add CODEOWNERS for review routing
    What

    Adds .gitlab/CODEOWNERS so GitLab auto-requests review from the right person for the files a merge request touches.

    Ownership is a starting point derived from git history:

    • default reviewer: @ih-abir
    • build / CI / tooling / docs: @jurjendevries
    • profile / key export / OpenBunker: @mmaaaazu
    • Nostr client scripts: @ih-abir + @mmaaaazu

    Note: this only enforces approval once branch protection on main has "Require approval from Code Owners" enabled (a separate team decision). Until then it just suggests reviewers. Adjust the mapping as the team sees fit.

  • MR !60 by Jurjen de Vries ·
    Docs
    document STRAPI_URL and default .env.example to the public CMS
    What

    Documents the value of STRAPI_URL where it was missing, so a fresh clone builds out of the box.

    • .env.example now defaults STRAPI_URL to the public CMS https://strapicms.wondersofwork.nl (the instance previews and production build against), with a comment for local Strapi.
    • CONTRIBUTING.md, AGENTS.md and README.md mention the value.

    This removes the onboarding gap where external / AI contributors could not build without knowing which Strapi to point at.

  • MR !59 by Jurjen de Vries ·
    Docs
    add contributing guide, community files, and AI context
    What

    Adds the open-source community and contributor docs the project was missing, and a context file for AI coding assistants.

    New
    • CONTRIBUTING.md — local setup (nvm + corepack + pnpm), the trunk-based
    • workflow, branch naming, Conventional Commits, rebasing, the CI gate, opening and reviewing merge requests, and a section for non-developer / AI-assisted contributors (with a getting-started for Claude Code, Codex, Cursor).

    • AGENTS.md — short, cross-tool context file AI assistants read to set up and
    • follow the workflow on their own.

    • LICENSE — MIT for the source code, with a scope note: content and design by
    • Wonders of Work are all rights reserved, and Nostr content belongs to its authors.

    • CODE_OF_CONDUCT.md — Contributor Covenant 2.1 (contact connect@wondersofwork.nl).
    • SECURITY.md — private vulnerability reporting.
    • THIRD-PARTY-LICENSES.md — attribution for the vendored bundles
    • (nostr-tools = Unlicense, snarkdown = MIT, node-qrcode = MIT).

    • .gitlab/ — a merge request template and bug / idea issue templates.
    Changed
    • README.md — replaced the "demo" stub with a real intro, quick start, and
    • links to the docs and licenses.

    • package.json — declares "license": "MIT".
    Notes

    Pure documentation, no code or behaviour changes. Formatting passes pnpm format:check.

  • MR !58 by Jurjen de Vries ·
    Chore
    introduce CI pipeline, commit hooks, and Node pinning
    What

    First step of the trunk-based workflow migration: a CI quality gate plus local commit hooks. Since main will auto-deploy to production, the pre-merge checks are the safety net.

    CI pipeline (.gitlab-ci.yml)

    Runs on every MR (and on branch pushes without an open MR):

    • formatprettier --check (blocking)
    • typecheckastro checknon-blocking for now: ~47 pre-existing errors to burn down, then drop allow_failure to make it blocking
    • test — redirects unit test (blocking)
    • buildastro build with STRAPI_URL (blocking)
    • commitlint — Conventional Commits on the MR's commits (blocking)
    Local hooks (husky + commitlint + lint-staged)
    • commit-msg enforces Conventional Commits
    • pre-commit runs prettier on staged files
    • The same commitlint check runs in CI as the real gate, since local hooks can be skipped
    Node pinned to 22.16.0
    • .nvmrc + the CI image match the version Cloudflare Pages builds with. Cloudflare reads .nvmrc too, so that file is the single source of truth — no local/CI/prod drift.
    • The hooks require Node 20+. After pulling this branch, run nvm install once.
    Formatting normalized
    • One-time prettier --write so the format gate is green (commit style: normalize formatting).
    • Vendored *.bundle.mjs and the unparseable inline scripts in Input.astro are excluded via .prettierignore.
    Note

    This MR is the first run of the new pipeline — it validates the gate itself. Expect typecheck to report errors without failing the pipeline.

See all merged changes on GitLab →