Skip to content

Make bibliographies read correctly in newsletters and feeds - #158

Merged
dknauss merged 3 commits into
mainfrom
claude/keen-tesla-49orwb
Oct 21, 2026
Merged

dknauss merged 3 commits into
mainfrom
claude/keen-tesla-49orwb

Conversation

@dknauss

@dknauss dknauss commented Oct 21, 2026

Copy link
Copy Markdown
Owner

Summary

Problem. In a Jetpack / WordPress.com newsletter, a Borges bibliography looked broken:

  • every reference was italic, so titles lost their own italics;
  • each entry ran on into "Cite / Export";
  • "Copy citation" was a button that did nothing;
  • the download links didn't work.

Cause. Email clients and feed readers show post content without the block's stylesheet and script, and the saved markup depended on both:

  • entries were <cite>, which browsers italicize by default;
  • the Cite / Export panel was a saved <details> with a JS button and data: download links.

Fixes (2, 3 and 4 from the investigation):

  1. Plain entries. save() writes each entry as <span class="bibliography-builder-entry-text">. Same class, so the CSS is unchanged. HTML defines <cite> as a work's title, not a whole reference, so this also fixes the semantics.
  2. The panel moves to view.js.
    • When Cite / Export is on, each <li> carries data-cite-text, -filename, -ris, -csl-json, and optional -bibtex/-biblatex.
    • view.js builds the same <details> panel on the page, with the same classes and data-cite-export-filename links, so the styles and the cite-export e2e selectors are unchanged.
    • Translated labels arrive in a data-cite-labels attribute from the existing render filter, so view.js still has no wp-i18n.
    • data- attributes survive the post-content HTML filter for every role, unlike a <script>. Post content gets smaller because the percent-encoded data: URIs are gone.
    • Trade-off: visitors without JavaScript no longer get the panel. The bibliography itself is unaffected.
  3. Email and feeds (includes/email-output.php):
    • The block registers a render_email_callback. That's the email renderer WooCommerce, MailPoet, Jetpack/WordPress.com newsletters and Newspack share; blocks without one get their saved HTML dropped into a table.
    • RSS/Atom feeds get the same output through a render_block filter.
    • Both drop the saved panel or its data-cite-* data, the scripts and COinS, and set entries upright with an inline style. That covers posts saved before this change too, wherever the email is built on the site.

Compatibility.

  • A new deprecated[0] freezes the unreleased intermediate shape (particle sort with <cite> and the saved panel). Older posts validate and switch to the new markup when next saved.
  • Fixes a bug from Sort names with particles per each style's demote setting #147: its 1.11.0 deprecation lacked jsonLdItemList: true. A 1.11.0 post whose order the particle sort changes would have opened as invalid. Fixed; the v13 fixture is regenerated and differs only in the JSON-LD.
  • The PHP save port (used by the write routes) matches byte for byte.

Which setups this covers:

  • Self-hosted with Jetpack, and WordPress.com-hosted: new and re-saved posts are clean in any email pipeline, including WordPress.com building the newsletter from synced content where Borges' PHP doesn't run.
  • Older posts: cleaned wherever the email is rendered on the site, through the callback.

Validation

  • npm run lint:js, lint:css, lint:i18n, format:check: pass
  • composer lint:php, composer analyze:php: pass
  • Jest: 1056 passed. The suites changed for the new contract are save, deprecations, locale-independence, and view (the built panel, translated labels, idempotence, clipboard).
  • PHPUnit: 455 pass. New tests:
    • EmailOutputTest: current and legacy markup (v07, v13, v14), repeated-author spans, the feed filter, and callback registration.
    • Frontend-label tests for data-cite-labels.
  • Save parity: fixtures regenerated from PHP; JS matches 20/20, byte-identical.
  • Deprecation fixtures: only v00 (current), v13 (the jsonLdItemList fix) and the new v14 changed.
  • npm run build and package:release succeed. view.js is 3,156 bytes (1,272 gzipped), up from 1,449. composer verify:metrics passes.
  • Not verified here: a real newsletter send. Worth one test send from a WordPress.com site after merge.

Checklist

  • Tests added/updated
  • CHANGELOG [Unreleased] entry
  • Docs updated (readme.txt, README, SPEC, CLAUDE.md, metrics)
  • Saved-content compatibility covered by deprecations

Generated by Claude Code

Email clients and feed readers show post content without the block's
stylesheet and script. Saved entries were <cite>s, italic by default,
so a whole reference turned italic and titles lost their italics; with
Cite / Export on, each entry also showed an expanded "Cite / Export",
a dead "Copy citation" button, and blocked data: download links.

- save() writes entries as <span class="bibliography-builder-entry-text">.
- save() no longer writes the Cite / Export panel. Each <li> carries
  data-cite-text, -filename, -ris, -csl-json, and optional -bibtex and
  -biblatex; view.js builds the same panel on the page. The render
  filter passes translated labels in data-cite-labels, so view.js still
  needs no wp-i18n. Post content shrinks: no percent-encoded data URIs.
- The block registers a render_email_callback (the email renderer that
  WooCommerce, MailPoet, Jetpack newsletters, and Newspack share), and
  feeds get the same output, so legacy posts arrive clean too: saved
  panel, scripts, and COinS dropped, entries upright.

A new deprecation freezes the unreleased intermediate shape. The 1.11.0
deprecation from the particle-sort change was missing jsonLdItemList,
so a 1.11.0 post whose order the particle sort changes would have opened
as invalid; fixed, and its fixture regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbVuMPN7YTcV27vwsG3LZ2
@codecov

codecov Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.38843% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.27%. Comparing base (6820574) to head (2352162).

Files with missing lines Patch % Lines
src/view.js 85.10% 0 Missing and 7 partials ⚠️
src/save-markup.js 66.66% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main     #158      +/-   ##
============================================
- Coverage     88.28%   88.27%   -0.02%     
  Complexity       22       22              
============================================
  Files            61       62       +1     
  Lines          7291     7359      +68     
  Branches        690      699       +9     
============================================
+ Hits           6437     6496      +59     
- Misses          291      292       +1     
- Partials        563      571       +8     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

The email callback wraps its output with the email editor's
Table_Wrapper_Helper when the renderer is loaded; a namespaced stub now
exercises that path. view.js gets a test for malformed label JSON and an
entry without a filename.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbVuMPN7YTcV27vwsG3LZ2
@dknauss
dknauss marked this pull request as ready for review October 21, 2026 18:40
@dknauss
dknauss merged commit 944f0bc into main Oct 21, 2026
19 checks passed
@dknauss
dknauss deleted the claude/keen-tesla-49orwb branch October 21, 2026 18:41
dknauss pushed a commit that referenced this pull request Oct 21, 2026
Resolves the metrics doc's list of included files (now twelve, with
csl-sanitize.php and email-output.php) and drops the two functions #158
deliberately removed, save_cite_export and save_export_link, from the
function inventory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbVuMPN7YTcV27vwsG3LZ2
@dknauss dknauss mentioned this pull request Oct 22, 2026
4 of 5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants