Open for review
🌱 Nothing is waiting for review right now. Want to help shape what comes next? Read CONTRIBUTING →
Recently shipped to production (main) wondersofwork.nl 🎉
-
Fixreconnect to relays whose connection failed or droppedWhat this changes, and why
If a relay's first connection attempt failed, or its connection dropped later, the site never tried that relay again during the visit, as long as another relay was up. relay.nostr.net fails its first handshake fairly often, and it is our most complete relay.
The reconnect code in
connect-relays.jsnever ran.oncloseremoved the relay fromwsConnectionsand then checkedwsConnections.has(url), which is always false. Now:- A failed or dropped connection is retried with the existing backoff (3 s, 6 s), up to 3 attempts. Relays that were removed from the list on purpose are not retried.
- A stale
onclosefrom an old socket no longer removes a newer, working connection. - The 60 s maintenance pass also retries relays that are down, once their attempt counter resets (every 5 min). Before, it only did this when no relay at all was working.
When a relay connects late, it still receives the requests that are already running, through the late-relay watcher in
What this affectsnostr-queue.js.This touches every page that uses Nostr, but only the relay connection handling. Nothing else changes.
How to try itOn the preview, open /nl/verhalen with DevTools open.
- Console: when a relay's first connection fails you should see
Reconnecting to wss://… in 3 seconds..., followed by a new attempt. - To force it, in Network → WS, block
relay.nostr.netfor the first load (request blocking), then unblock it. The relay should come back within a few seconds.
In a test with a simulated failing first handshake, production never reconnected. This branch reconnected on attempt 3 and relay.nostr.net still delivered 15 events for requests already in progress.
Before you ask for review- [x] Branch named
feat/…,fix/…,chore/…,docs/…orhotfix/… - [x] Commit messages follow Conventional Commits
- [x]
pnpm formathas been run and the checks are green - [ ] Tried it on the preview link
- [x] Rebased on the latest
main
-
Fixread Nostr content from relays that still hold itWhat this changes, and why
Most stories, calendar events and profiles stopped showing. The site read from four relays, and on 2026-09-30 none of them still had the content: relay.nsec.app is unreachable, nos.lol and relay.primal.net keep our events for only about a day, and relay.damus.io accepts connections but mostly never answers. The content still exists on other relays.
- New relay list: relay.nostr.net, relay.ditto.pub, nostr.oxtr.dev, nostr.mom, nostr.data.haus. Each was measured to hold our content and to answer.
- purplepag.es is added for profiles only (kinds 0/3/10002). Other queries and posts skip it (
window.relaysFor). - Subscriptions stay open 6 s after the first relay answers instead of 2.5 s, so slower relays are no longer cut off.
- The relay hints in "copy ID" (naddr) now point to relays that actually hold the events.
- Event detail showed "not found" at random: it stopped waiting while a relay was still connecting, and a duplicate load could leave the page hidden. RSVP links ("Meet <name> at") now pass the author, so events made in other Nostr calendar apps (no WoW tag) open too.
These changes touch every Nostr read and publish (stories, events, profiles, reactions, deletions), so posting and deleting also go to the new relays. Not changed: the OpenBunker login relays. The OpenBunker API still returns relay.nsec.app, but that is a separate change. The "empty" or "loaded" state can now appear up to 6 s after the first relay answers, but only when a relay is slow.
How to try itOn the preview link:
- /nl/verhalen and /nl/agenda should show all stories and events (in local tests: 22 stories and 11 events, where production showed 0–1).
- /nl/wonderful-people should show all profiles with names and avatars (39 of 39, where production showed 1).
- Open a profile with RSVPs and click the events under "Meet <name> at". Each should open every time.
- In DevTools > Network > WS: purplepag.es should only get
kinds:[0]REQs. - While logged in: post and delete a test story. Publishing and deleting have not been tested.
- [x] Branch named
feat/…,fix/…,chore/…,docs/…orhotfix/… - [x] Commit messages follow Conventional Commits
- [x]
pnpm formathas been run and the checks are green - [ ] Tried it on the preview link
- [x] Rebased on the latest
main
-
Fixdefault location room selector visibilityWhat this changes, and why
Fixed create an event default location room selector visibility
What this affects
How to try itsrc/pages/[...locale]/create-event.astrogo to create event page and select the default location
-
Featureadd sml-spaces variant to cardGrid to generate random S M L spacesWhat this changes, and why
Add sml-spaces variants to CardGrid to generate 3 random spaces by serialy small, medium, large spaces
What this affects
How to try itsrc/components/blocks/CardGrid.astrogo to homepage after Discover section you will find the section. you can regenerate or create from strapi using CardGrid page component and selecting sml-spaces from variants selector
-
Chorerename default-* UI fallbacks to fallback-*What this changes, and why
Renames the UI fallback assets from default-* to fallback-* so they are clearly not editorial media, and so the upcoming hardcoded/i18n lint can exclude fallback-* by convention.
- public/images/default-avatar.svg -> fallback-avatar.svg
- public/images/default-placeholder.svg -> fallback-placeholder.svg
- public/images/loader.svg -> fallback-loader.svg
- all references updated across .astro and public/scripts (24+ refs), zero old refs left.
og-default.jpg is left as-is (Open Graph share image, different purpose).
What this affectsImage file names under public/images and every reference to them. No behaviour change — same images, new names. No editorial content.
How to try itOpen a preview: avatars, placeholders and the loader still render (they now load from /images/fallback-*.svg).
-
Fixhardcoded text replace with cmsWhat this changes, and why
Completes the hardcoded-copy → Strapi migration so every user-facing string is content-managed (editable and translatable in Strapi instead of baked into code). This covers the bulk of the hardcoded-copy backlog, including copy hidden in client scripts that the grep-based lint:hardcoded could not see.
English is preserved verbatim in the EN locale; the frontend now reads whatever Strapi serves per locale (nl-NL is populated), with an English fallback kept as the safety net.
What this affects- public/scripts/* — all client libs: raw literals → window.__texts / window.profileUi / window.eventUi lookups; two dead files removed (user-metadata.js, profile.js), superseded by check-user-metadata.js
- src/components/* — form fields, editor, navbar, pagination, reactions, room/tag selectors, loading, and blocks (audio/card/categories/newsletter/offer/quote/richtext), plus ExportKeySection
- src/pages/[...locale]/* — agenda details, create-event, edit-profile, login, and event/story/profile previews
- src/layouts/Layout.astro — injects window.__texts for client scripts
- src/utils/getGlobals.ts + get{Login,Profile,Event}UiConfig.ts — expose the per-feature config shapes
No Strapi schema changes in this MR — schema and content already live in production.
How to try itOn the preview link, switch locale (nl / en) and confirm the strings follow:
- Newsletter status messages, event/story preview copy, profile export-key labels
- Form field labels/placeholders, editor toolbar options, loading and error states
- [x] Branch named fix/…
- [x] Commit messages follow Conventional Commits
- [x] pnpm format run, checks green
- [x] Tried it on the preview link
- [x] Rebased on the latest main
-
Fixreplace newsletter custom icon with image mediaWhat this changes, and why
update newsletter icon logic
What this affects
How to try itsrc/components/blocks/Newsletter.astrogo to coworking page
-
Fixharden markdown editor and add fenced code blocksWhat this changes, and why
Hardening pass on the custom markdown editor used by the event and story description forms (MarkdownEditor2, a contenteditable WYSIWYG with a markdown bridge). Fixes from a security audit plus extended edge-case testing:
- HTML injection and attribute breakout in the inline parser
- Multi-line blockquotes kept only the first
>line - Fenced code blocks were mangled on save/reload
- Mixed bold+italic (
**a*b*c**) lost the literal stars - Trailing-space growth on every save (soft breaks)
- Duplicate
.mde-editor pCSS rule - Autolinks (
<https://...>) eaten as HTML tags - Link titles (
[x](url "title")) broke the URL attribute - Nested lists and continuation lines were dropped
- Missing constructs vs the renderer: h1/h5/h6, inline code,
<hr>,
images,
1)lists, backslash escapes, task listsAdds fenced code blocks with sugar-high syntax highlighting (GitHub-dark palette). Interactive behavior hardened: Enter splits blocks into new paragraphs (Shift+Enter = soft break), the code button is selection-aware (only the selection becomes a block; a collapsed caret inserts a fresh one), empty paste no longer destroys the selection, Enter in an empty list item keeps the list, linking over a link is refused.
Round-trips are byte-stable, escaping is minimal so nostr clients and marked never show stray backslashes, and the editor was fuzzed (unicode, deep nesting, huge inputs) without crashes, loss or XSS.
What this affectssrc/components/MarkdownEditor2.astro- the editor (used by bothpublic/scripts/render-markdown.js- public renderer, imports thepublic/scripts/sugar-high.bundle.mjs- new vendored bundlesrc/styles/_base.scss-.mde-editorstyles and--sh-*tokenspackage.json/pnpm-lock.yaml-sugar-high@^2.0.0dependencydocs/markdown-editor.md- new editor documentation
create-event and create-story forms)
sugar-high bundle for fenced code highlighting
Review carefully: inline HTML escaping and URL sanitization (same allowlist as the public renderer), block-start escaping, and the Range-based DOM surgery in the Enter / code-button paths.
How to try itOn the preview link, open "Create event" (or a story):
- Type a paragraph, press Enter - a new paragraph starts; Shift+Enter
- Select text and press
</>- only the selection becomes a code - On an empty line,
</>starts a fresh code block. Enter adds lines. - Fenced code with a language (e.g. ```js) renders highlighted on the
- Paste an image (no text) - the selection is preserved.
makes a soft line break. Save and reload: content is byte-identical.
block; surrounding text stays put.
public event page.
- [x] Branch named
feat/...,fix/...,chore/...,docs/...orhotfix/... - [x] Commit messages follow Conventional Commits
- [x]
pnpm formathas been run and the checks are green - [ ] Tried it on the preview link
- [x] Rebased on the latest
main
-
Fixpurple form field borders instead of peachWhat this changes
Form fields used the peach
$secondary-lightest(#fcdccc) border, which reads as light orange next to the purple brand palette. All form field borders are now$primary-lighter(#c1b3d8, lila) — same lightness, so the outline stays soft.src/styles/_base.scss:60— sharedinput-basemixin, so.input,.input-sm,.selector,.selector-sm,.textarea,.textarea-sm,.input-icon-inand friends all follow. Covers the newsletter signup, contact form, offer/quote form and booking form.src/components/DateTimePicker.astro,src/components/BookingForm.astro,src/components/blocks/Offer.astro— date/time dropdown panels (Offer also had aborder-secondary-light/20panel border and divider).src/components/MarkdownEditor.astro,src/components/MarkdownEditor2.astro— editor frame, toolbar and floating toolbars.
Left untouched on purpose:
How to trybtn-border-secondarybuttons, peach *backgrounds* (Specs, CapsuleGrid, StepsGrid, Booking, Spotlight), and the markdown prosehr/blockquote borders in_base.scss— those are content, not form chrome.1. Open the preview deploy for this MR. 2. Home page → newsletter block ("Abonneer je op onze nieuwsbrief"): the e-mail field border is lila, not peach. 3.
What this affects/nl/offerte(offer form): all text fields, the textareas, the date/time picker and its dropdown panel show the same lila border. 4. Booking block on the home page: the number-of-people and date/time fields plus the dropdown panel match. 5. Focus a field — the focus ring is stillring-primary-light, unchanged.CSS only, no markup or logic changes; 11 lines across 6 files. No content, Strapi or route changes. Both locales, all pages containing forms. Everything is a colour token swap between existing palette variables, so no new colours are introduced.
-
CIadd ESLint base (lint:code)What this changes, and why
Adds an ESLint base as the foundation for real code-quality linting (and, next, the mature i18n no-literal-string rule that will replace the grep-based lint:hardcoded). ESLint understands the JS/TS/Astro AST, so it catches bugs and bad patterns a regex cannot.
- eslint + typescript-eslint + eslint-plugin-astro, flat config (eslint.config.js), non-type-checked recommended sets (fast, no Strapi needed).
- New pnpm lint script and lint:code CI job, allow_failure (orange warning) like lint:types/lint:hardcoded.
- no-explicit-any turned off for now (Strapi/Nostr data is loosely typed; separate effort); unused-vars as warnings during rollout.
- Currently ~123 findings (empty blocks, unused vars, unused expressions, useless escapes) — real smells, non-blocking.
eslint.config.js (new), package.json, pnpm-lock.yaml, .gitlab-ci.yml (new lint:code job), CONTRIBUTING checks table. No application code.
How to try itpnpm lint locally, or the lint:code job on this MR (orange warning, non-blocking).