Heisenberg v0.0.2 - email personalization, builder phases, URL-filter XSS fix
What's new in 0.0.2
This release is dominated by the email subsystem and two fixes that touch every surface.
✉️ Email personalization (host variables + admin batch export)
- Host-defined email variables: register any dotted key with a typed formatter contract and a safe non-secret sample. Six built-in types ship (
text,url,email,number,boolean,date). - Strict context-aware interpolation before rich-text sanitization / URL filtering, with value-free aggregated errors for unknown tokens, missing values, formatter failures, and target mismatches.
- Per-recipient
EmailRendererandHeisenbergMailableseams across MIME subject, HTML, plain text, size accounting, and CID embeds — while preserving legacy token-free calls. - Sample-only public/editor preview, size, and single HTML/EML export. Runtime maps are never read from author-facing GET query strings, bodies, or headers.
- Email-only authoring picker (subject, rich text, compatible text settings, URL settings) inserting literal
{{ dotted.key }}tokens via text nodes /setRangeText, neverinnerHTML. - Admin-only, all-or-nothing batch ZIP export (
email.generate, defaultadmin) of exactly N recipients × requested locales as HTML or EML. Recipients are explicit value maps; Heisenberg still does not own SMTP, subscribers, campaigns, or recipient discovery. - Usage docs (
docs/email-personalization.md) and compile-checked host examples (examples/EmailVariables/).
🧱 Builder / editor
- Single-row content model — one post carries every configured locale instead of separate translation rows.
- Native threaded comments, moderation, a public thread API, and shared-surface route wiring.
- SEO persistence, scoring, sitemap, hreflang, and opt-in public post routes.
- User-saved block patterns, quick-inserter browse-all, number-stepper UI, gradient values, nested/per-state fonts, and expanded column/inspector controls.
- Responsive featured-image rendering in the public preview.
- Deep published-config merging plus
heisenberg:config-diff.
🔒 Security
javascript:URL-filter XSS bypass fix inBlockRenderer::safeUrl()/NullMediaResolver::safeUrl(): control characters inside the scheme are stripped before parsing (e.g.java\tscript:…), with regression coverage for all obfuscated variants.target="_blank"now emitsrel="noopener noreferrer".ThemeRepository::save()writes withLOCK_EX.
🐛 Notable fixes
- Autosave no longer reverts queued status/slug/date; schedule/publish timestamps no longer drift with viewer timezone.
- Email rendering matches authored content, including translated block attributes.
- Discussion settings target the real toggle input; disabled commenting keeps a frozen thread while hosts can choose how to display it.
- Duplicated blocks get fresh IDs; navigator selection, child toolbar targeting, column flex-basis, heading specificity, icon theming, and font-family quoting fixed.
- AI assistant icon lookup, translation preservation, prompt-size limits, composer height, SEO scoring and sitemap locale assumptions.
Built against
- PHP 8.2 / 8.3 / 8.4 × Laravel 11 / 12 / 13, prefer-lowest + prefer-stable — all green on
14c9037f. - Full suite: 1539 tests / 6121 assertions (email wave alone: 8
feat(email)commits).
Install
composer require heisenberg/heisenberg
php artisan migrate
php artisan storage:linkOpen /editor and start writing.