tfrere HF Staff commited on
Commit
727352b
·
1 Parent(s): 2ea3987

fix: bibliography not appearing - extract HTML before generateHTML

Browse files

The renderedhtml attribute can contain > chars (valid in quoted HTML
attributes) which broke the [^>]* regex in postProcess. Now we extract
the bibliography HTML from the TipTap JSON tree before calling
generateHTML, clear the attribute to keep the output clean, and inject
the raw HTML in postProcess using the pre-extracted string.

Made-with: Cursor

backend/src/publisher/html-renderer.ts CHANGED
@@ -32,8 +32,12 @@ export function renderArticleHTML(
32
  cssArticle: string
33
  ): string {
34
  const extensions = getServerExtensions();
 
 
 
 
35
  const bodyHtml = generateHTML(json as any, extensions);
36
- const enrichedBody = postProcess(bodyHtml);
37
 
38
  const authorNames = meta.authors.map((a) => a.name);
39
  const authorsStr = authorNames.join(", ");
@@ -262,13 +266,30 @@ dialog.lightbox img {
262
  </html>`;
263
  }
264
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
  /**
266
  * Post-process the raw generateHTML output:
 
267
  * - Transform accordion divs into <details><summary>
268
  * - Transform htmlEmbed into iframes
269
- * - Add semantic structure where possible
270
  */
271
- function postProcess(html: string): string {
272
  let result = html;
273
 
274
  // Accordion: div[data-component="accordion"] → <details><summary>
@@ -285,23 +306,17 @@ function postProcess(html: string): string {
285
  }
286
  );
287
 
288
- // Bibliography: inject renderedhtml attribute as actual HTML content
 
 
289
  result = result.replace(
290
  /<div([^>]*data-type="bibliography"[^>]*)><\/div>/gi,
291
  (_match, attrs: string) => {
292
- const htmlMatch = attrs.match(/renderedhtml="([^"]*)"/i);
293
  const cleanAttrs = attrs.replace(/\s*renderedhtml="[^"]*"/i, "");
294
- if (!htmlMatch || !htmlMatch[1]) {
295
- return `<div${cleanAttrs}><p class="bibliography-empty">No citations</p></div>`;
296
  }
297
- const decoded = htmlMatch[1]
298
- .replace(/&amp;/g, "&")
299
- .replace(/&lt;/g, "<")
300
- .replace(/&gt;/g, ">")
301
- .replace(/&quot;/g, '"')
302
- .replace(/&#039;/g, "'")
303
- .replace(/&#x27;/g, "'");
304
- return `<div${cleanAttrs}><h2 class="bibliography-title">References</h2><div class="bibliography-content">${decoded}</div></div>`;
305
  }
306
  );
307
 
 
32
  cssArticle: string
33
  ): string {
34
  const extensions = getServerExtensions();
35
+
36
+ // Extract bibliography HTML before generateHTML escapes it as an attribute
37
+ const biblioHtml = extractBibliographyHtml(json);
38
+
39
  const bodyHtml = generateHTML(json as any, extensions);
40
+ const enrichedBody = postProcess(bodyHtml, biblioHtml);
41
 
42
  const authorNames = meta.authors.map((a) => a.name);
43
  const authorsStr = authorNames.join(", ");
 
266
  </html>`;
267
  }
268
 
269
+ /**
270
+ * Walk the TipTap JSON tree to find a bibliography node, extract its
271
+ * renderedHtml attribute, and clear it so generateHTML outputs a clean div.
272
+ */
273
+ function extractBibliographyHtml(json: Record<string, unknown>): string {
274
+ const content = (json as any)?.content;
275
+ if (!Array.isArray(content)) return "";
276
+ for (const node of content) {
277
+ if (node.type === "bibliography") {
278
+ const html = node.attrs?.renderedHtml || "";
279
+ if (node.attrs) node.attrs.renderedHtml = "";
280
+ return html;
281
+ }
282
+ }
283
+ return "";
284
+ }
285
+
286
  /**
287
  * Post-process the raw generateHTML output:
288
+ * - Inject bibliography HTML (extracted before generateHTML)
289
  * - Transform accordion divs into <details><summary>
290
  * - Transform htmlEmbed into iframes
 
291
  */
292
+ function postProcess(html: string, biblioHtml: string): string {
293
  let result = html;
294
 
295
  // Accordion: div[data-component="accordion"] → <details><summary>
 
306
  }
307
  );
308
 
309
+ // Bibliography: replace empty placeholder with the pre-extracted HTML.
310
+ // renderedHtml was cleared before generateHTML, so the attribute is now
311
+ // empty and the div is clean (no > chars breaking the regex).
312
  result = result.replace(
313
  /<div([^>]*data-type="bibliography"[^>]*)><\/div>/gi,
314
  (_match, attrs: string) => {
 
315
  const cleanAttrs = attrs.replace(/\s*renderedhtml="[^"]*"/i, "");
316
+ if (biblioHtml) {
317
+ return `<div${cleanAttrs}><h2 class="bibliography-title">References</h2><div class="bibliography-content">${biblioHtml}</div></div>`;
318
  }
319
+ return `<div${cleanAttrs}><p class="bibliography-empty">No citations</p></div>`;
 
 
 
 
 
 
 
320
  }
321
  );
322