domma-cms 0.47.0 → 0.47.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domma-cms",
3
- "version": "0.47.0",
3
+ "version": "0.47.1",
4
4
  "description": "File-based CMS powered by Domma and Fastify. Run npx domma-cms my-site to create a new project.",
5
5
  "type": "module",
6
6
  "main": "server/server.js",
@@ -1668,7 +1668,7 @@ function processEffectsBlocks(markdown) {
1668
1668
  if (body === null) return `<div class="${classes.join(' ')}"></div>`;
1669
1669
  // Restore code placeholders before the nested parse (tabs-handler
1670
1670
  // pattern) so code spans/fences render as <code>, not raw backticks.
1671
- const innerHtml = marked.parse(processCardBlocks(processGridBlocks(restore(body.trim()))));
1671
+ const innerHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(body.trim())))));
1672
1672
  return `<div class="${classes.join(' ')}">${innerHtml}</div>\n`;
1673
1673
  });
1674
1674
 
@@ -1685,7 +1685,7 @@ function processEffectsBlocks(markdown) {
1685
1685
  if (!hasActions) {
1686
1686
  // Restore code placeholders before the nested parse (tabs-handler
1687
1687
  // pattern) so code spans/fences render as <code>, not raw backticks.
1688
- const innerHtml = marked.parse(processCardBlocks(processGridBlocks(restore(body.trim()))));
1688
+ const innerHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(body.trim())))));
1689
1689
  return `<div class="dm-fx-scribe"${dataAttrs}>${innerHtml}</div>\n`;
1690
1690
  }
1691
1691
  const actions = [];
@@ -1724,7 +1724,7 @@ function processEffectsBlocks(markdown) {
1724
1724
  const dataAttrs = Object.entries(attrs).map(([k, v]) => ` data-fx-${k}="${v}"`).join('');
1725
1725
  // Restore code placeholders before the nested parse (tabs-handler
1726
1726
  // pattern) so code spans/fences render as <code>, not raw backticks.
1727
- const innerHtml = marked.parse(processCardBlocks(processGridBlocks(restore(body.trim()))));
1727
+ const innerHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(body.trim())))));
1728
1728
  return `<div class="dm-fx-${name}"${dataAttrs}>${innerHtml}</div>\n`;
1729
1729
  });
1730
1730
  }
@@ -1741,7 +1741,7 @@ function processEffectsBlocks(markdown) {
1741
1741
  }
1742
1742
  // Restore code placeholders before the nested parse (tabs-handler
1743
1743
  // pattern) so code spans/fences render as <code>, not raw backticks.
1744
- const innerHtml = marked.parse(processCardBlocks(processGridBlocks(restore(body.trim()))));
1744
+ const innerHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(body.trim())))));
1745
1745
  return `<div class="dm-fx-ticker-tape" data-fx-mode="container"${dataAttrs}>${innerHtml}</div>\n`;
1746
1746
  });
1747
1747
 
@@ -1759,7 +1759,7 @@ function processEffectsBlocks(markdown) {
1759
1759
  }
1760
1760
  // Restore code placeholders before the nested parse (tabs-handler
1761
1761
  // pattern) so code spans/fences render as <code>, not raw backticks.
1762
- const innerHtml = marked.parse(processCardBlocks(processGridBlocks(restore(body.trim()))));
1762
+ const innerHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(body.trim())))));
1763
1763
  return `<div class="dm-fx-${name}" data-fx-mode="container"${dataAttrs}>${innerHtml}</div>\n`;
1764
1764
  });
1765
1765
  }
@@ -1772,7 +1772,7 @@ function processEffectsBlocks(markdown) {
1772
1772
  if (attrs.duration) classes.push(`animate-duration-${attrs.duration}`);
1773
1773
  if (attrs.delay) classes.push(`animate-delay-${attrs.delay}`);
1774
1774
  if (attrs.repeat) classes.push(`animate-${attrs.repeat}`);
1775
- return `<div class="${classes.join(' ')}">${marked.parse(processCardBlocks(processGridBlocks(restore(body.trim()))))}</div>\n`;
1775
+ return `<div class="${classes.join(' ')}">${marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(body.trim())))))}</div>\n`;
1776
1776
  });
1777
1777
 
1778
1778
  apply('ambient', (attrStr, body) => {
@@ -1782,7 +1782,7 @@ function processEffectsBlocks(markdown) {
1782
1782
  if (attrs.type) classes.push(`bg-ambient-${attrs.type}`);
1783
1783
  if (attrs.speed) classes.push(`bg-ambient-${attrs.speed}`);
1784
1784
  if (attrs.intensity) classes.push(`bg-ambient-${attrs.intensity}`);
1785
- return `<div class="${classes.join(' ')}">${marked.parse(processCardBlocks(processGridBlocks(restore(body.trim()))))}</div>\n`;
1785
+ return `<div class="${classes.join(' ')}">${marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(body.trim())))))}</div>\n`;
1786
1786
  });
1787
1787
 
1788
1788
  return restore(result);
@@ -1848,7 +1848,7 @@ function processGridBlocks(markdown) {
1848
1848
  // processCardBlocks, which creates its own empty store and fails to
1849
1849
  // decode them, producing the literal string "undefined" in place of
1850
1850
  // whatever was in <pre>, ``` fences, or `inline code`.
1851
- return `<div${cls}${id}${injected.effectDataAttrs}>${marked.parse(processCardBlocks(restore(body.trim())))}</div>`;
1851
+ return `<div${cls}${id}${injected.effectDataAttrs}>${marked.parse(processCardBlocks(processInlineShortcodes(restore(body.trim()))))}</div>`;
1852
1852
  }
1853
1853
  );
1854
1854
 
@@ -2148,7 +2148,7 @@ LAYOUT_RENDERERS['basic'] = (attrs, body) => {
2148
2148
  LAYOUT_RENDERERS['header-body'] = (attrs, body) => {
2149
2149
  const title = typeof attrs.title === 'string' ? attrs.title.trim() : '';
2150
2150
  const hdr = title ? `<div class="card-header"><div class="card-title">${escapeAttr(title)}</div></div>` : '';
2151
- const bodyHtml = marked.parse(processGridBlocks(body.trim()));
2151
+ const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
2152
2152
  return `${cardRoot(attrs)}${hdr}<div class="card-body">${bodyHtml}</div>${cardFooterAttr(attrs)}</div>\n`;
2153
2153
  };
2154
2154
 
@@ -2156,14 +2156,14 @@ LAYOUT_RENDERERS['header-body-footer'] = (attrs, body) => {
2156
2156
  const title = typeof attrs.title === 'string' ? attrs.title.trim() : '';
2157
2157
  const footer = typeof attrs.footer === 'string' ? attrs.footer.trim() : '';
2158
2158
  const hdr = title ? `<div class="card-header"><div class="card-title">${escapeAttr(title)}</div></div>` : '';
2159
- const bodyHtml = marked.parse(processGridBlocks(body.trim()));
2159
+ const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
2160
2160
  const ftr = footer ? `<div class="card-footer">${escapeAttr(footer)}</div>` : '';
2161
2161
  return `${cardRoot(attrs)}${hdr}<div class="card-body">${bodyHtml}</div>${ftr}</div>\n`;
2162
2162
  };
2163
2163
 
2164
2164
  LAYOUT_RENDERERS['no-header-footer'] = (attrs, body) => {
2165
2165
  const footer = typeof attrs.footer === 'string' ? attrs.footer.trim() : '';
2166
- const bodyHtml = marked.parse(processGridBlocks(body.trim()));
2166
+ const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
2167
2167
  const ftr = footer ? `<div class="card-footer">${escapeAttr(footer)}</div>` : '';
2168
2168
  return `${cardRoot(attrs)}<div class="card-body">${bodyHtml}</div>${ftr}</div>\n`;
2169
2169
  };
@@ -2174,7 +2174,7 @@ LAYOUT_RENDERERS['icon-top'] = (attrs, body) => {
2174
2174
  const icon = typeof attrs.icon === 'string' ? attrs.icon.trim() : '';
2175
2175
  const iconHtml = icon ? `<span data-icon="${escapeAttr(icon)}" class="card-icon"></span>` : '';
2176
2176
  const subHtml = subtitle ? `<div class="card-subtitle">${escapeAttr(subtitle)}</div>` : '';
2177
- const bodyHtml = marked.parse(processGridBlocks(body.trim()));
2177
+ const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
2178
2178
  return `${cardRoot(attrs)}
2179
2179
  <div class="card-header card-header-icon-stacked">
2180
2180
  <div class="card-avatar-wrap">${iconHtml}<div class="card-title">${escapeAttr(title)}</div>${subHtml}</div>
@@ -2188,7 +2188,7 @@ LAYOUT_RENDERERS['icon-inline'] = (attrs, body) => {
2188
2188
  const icon = typeof attrs.icon === 'string' ? attrs.icon.trim() : '';
2189
2189
  const iconHtml = icon ? `<span data-icon="${escapeAttr(icon)}"></span>` : '';
2190
2190
  const subHtml = subtitle ? `<div class="card-subtitle">${escapeAttr(subtitle)}</div>` : '';
2191
- const bodyHtml = marked.parse(processGridBlocks(body.trim()));
2191
+ const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
2192
2192
  return `${cardRoot(attrs)}
2193
2193
  <div class="card-header card-header-icon-inline">
2194
2194
  ${iconHtml}<div class="card-header-content"><div class="card-title">${escapeAttr(title)}</div>${subHtml}</div>
@@ -2199,13 +2199,13 @@ LAYOUT_RENDERERS['icon-inline'] = (attrs, body) => {
2199
2199
  LAYOUT_RENDERERS['image-top'] = (attrs, body) => {
2200
2200
  const title = typeof attrs.title === 'string' ? attrs.title.trim() : '';
2201
2201
  const hdr = title ? `<div class="card-header"><div class="card-title">${escapeAttr(title)}</div></div>` : '';
2202
- const bodyHtml = marked.parse(processGridBlocks(body.trim()));
2202
+ const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
2203
2203
  return `${cardRoot(attrs)}<div class="card-img-top"${imgStyle(attrs)}></div>${hdr}<div class="card-body">${bodyHtml}</div>${cardFooterAttr(attrs)}</div>\n`;
2204
2204
  };
2205
2205
 
2206
2206
  LAYOUT_RENDERERS['image-overlay'] = (attrs, body) => {
2207
2207
  const title = typeof attrs.title === 'string' ? attrs.title.trim() : '';
2208
- const bodyHtml = marked.parse(processGridBlocks(body.trim()));
2208
+ const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
2209
2209
  return `${cardRoot(attrs)}
2210
2210
  <div class="card-img-overlay"${imgStyle(attrs)}>
2211
2211
  <div class="card-overlay-text"><div class="card-title">${escapeAttr(title)}</div></div>
@@ -2215,14 +2215,14 @@ LAYOUT_RENDERERS['image-overlay'] = (attrs, body) => {
2215
2215
 
2216
2216
  LAYOUT_RENDERERS['thumb-left'] = (attrs, body) => {
2217
2217
  const title = typeof attrs.title === 'string' ? attrs.title.trim() : '';
2218
- const bodyHtml = marked.parse(processGridBlocks(body.trim()));
2218
+ const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
2219
2219
  return `${cardRoot(attrs, ['card-layout-thumb-left'])}<div class="card-img-left"${imgStyle(attrs)}></div>
2220
2220
  <div class="card-body">${title ? `<div class="card-title">${escapeAttr(title)}</div>` : ''}${bodyHtml}</div>${cardFooterAttr(attrs)}</div>\n`;
2221
2221
  };
2222
2222
 
2223
2223
  LAYOUT_RENDERERS['thumb-right'] = (attrs, body) => {
2224
2224
  const title = typeof attrs.title === 'string' ? attrs.title.trim() : '';
2225
- const bodyHtml = marked.parse(processGridBlocks(body.trim()));
2225
+ const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
2226
2226
  return `${cardRoot(attrs, ['card-layout-thumb-right'])}
2227
2227
  <div class="card-body">${title ? `<div class="card-title">${escapeAttr(title)}</div>` : ''}${bodyHtml}</div>
2228
2228
  <div class="card-img-right"${imgStyle(attrs)}></div></div>\n`;
@@ -2230,14 +2230,14 @@ LAYOUT_RENDERERS['thumb-right'] = (attrs, body) => {
2230
2230
 
2231
2231
  LAYOUT_RENDERERS['wide-left-image'] = (attrs, body) => {
2232
2232
  const title = typeof attrs.title === 'string' ? attrs.title.trim() : '';
2233
- const bodyHtml = marked.parse(processGridBlocks(body.trim()));
2233
+ const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
2234
2234
  return `${cardRoot(attrs, ['card-layout-horizontal'])}<div class="card-img-wide"${imgStyle(attrs)}></div>
2235
2235
  <div class="card-body">${title ? `<div class="card-title">${escapeAttr(title)}</div>` : ''}${bodyHtml}${cardFooterAttr(attrs)}</div></div>\n`;
2236
2236
  };
2237
2237
 
2238
2238
  LAYOUT_RENDERERS['full-bg'] = (attrs, body) => {
2239
2239
  const title = typeof attrs.title === 'string' ? attrs.title.trim() : '';
2240
- const bodyHtml = marked.parse(processGridBlocks(body.trim()));
2240
+ const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
2241
2241
  return `${cardRoot(attrs, ['dm-card-full-bg'])}<div class="card-img-top"${imgStyle(attrs)}></div>
2242
2242
  <div class="card-body">${title ? `<div class="card-title">${escapeAttr(title)}</div>` : ''}${bodyHtml}</div>${cardFooterAttr(attrs)}</div>\n`;
2243
2243
  };
@@ -2245,7 +2245,7 @@ LAYOUT_RENDERERS['full-bg'] = (attrs, body) => {
2245
2245
  LAYOUT_RENDERERS['split-half'] = (attrs, body) => {
2246
2246
  const title = typeof attrs.title === 'string' ? attrs.title.trim() : '';
2247
2247
  const gradient = typeof attrs.gradient === 'string' ? attrs.gradient.trim() : 'indigo';
2248
- const bodyHtml = marked.parse(processGridBlocks(body.trim()));
2248
+ const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
2249
2249
  return `${cardRoot(attrs, ['card-layout-split'])}<div class="card-split-left card-gradient-${escapeAttr(gradient)}"${imgStyle(attrs)}></div>
2250
2250
  <div class="card-split-right"><div class="card-body">${title ? `<div class="card-title">${escapeAttr(title)}</div>` : ''}${bodyHtml}</div></div></div>\n`;
2251
2251
  };
@@ -2628,6 +2628,29 @@ function renderLegacyCard(attrs, body, markedInstance, escAttr) {
2628
2628
  * @param {string} markdown
2629
2629
  * @returns {string}
2630
2630
  */
2631
+ /**
2632
+ * Resolve the inline shortcodes in a nested body BEFORE it reaches
2633
+ * marked.parse.
2634
+ *
2635
+ * marked HTML-escapes the quotes in any `[tag attr="x"]` still sitting in the
2636
+ * text, so a tag processed after it sees `attr=&quot;x&quot;` and loses every
2637
+ * attribute - `[text size="lg"]` inside a `[tab]` or a `[card]` rendered
2638
+ * unstyled, and `[button href="/x"]` came out with `href="true"`. The
2639
+ * top-level pipeline runs these five before the container tags for exactly
2640
+ * that reason (see parseMarkdown); every container that renders a nested body
2641
+ * has to match the order rather than invent its own.
2642
+ *
2643
+ * Idempotent: a body whose inline tags are already HTML has none left to match.
2644
+ *
2645
+ * @param {string} markdown
2646
+ * @returns {string}
2647
+ */
2648
+ function processInlineShortcodes(markdown) {
2649
+ return processCtaBlocks(processLinkBlocks(processButtonBlocks(
2650
+ processTextBlocks(processBadgeBlocks(markdown))
2651
+ )));
2652
+ }
2653
+
2631
2654
  function processCardBlocks(markdown) {
2632
2655
  const {scrubbed, restore} = scrubCodeRegions(markdown);
2633
2656
  return restore(scrubbed.replace(
@@ -2694,7 +2717,7 @@ function processTabsBlocks(markdown) {
2694
2717
  // so fenced code blocks render as <pre><code> rather than surviving as raw text
2695
2718
  // that would confuse subsequent scrubCodeRegions calls.
2696
2719
  const restoredBody = restore(tabBody.trim());
2697
- const bodyHtml = marked.parse(processCardBlocks(processGridBlocks(restoredBody)));
2720
+ const bodyHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restoredBody))));
2698
2721
  const icon = tabAttrs.icon ? tabAttrs.icon.trim() : '';
2699
2722
  items.push({title, icon, paneId, bodyHtml, first: tabIdx === 1});
2700
2723
  });
@@ -2749,7 +2772,7 @@ function processAccordionBlocks(markdown) {
2749
2772
  body.replace(/\[item([^\]]*)\]([\s\S]*?)\[\/item\]/gi, (__, itemAttr, itemBody) => {
2750
2773
  const itemAttrs = parseShortcodeAttrs(itemAttr);
2751
2774
  const title = itemAttrs.title || 'Item';
2752
- const bodyHtml = marked.parse(processCardBlocks(processGridBlocks(restore(itemBody.trim()))));
2775
+ const bodyHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(itemBody.trim())))));
2753
2776
  items += (
2754
2777
  `<div class="accordion-item">\n` +
2755
2778
  ` <h3 class="accordion-header"><button class="accordion-button" type="button">${escapeAttr(title)}` +
@@ -2813,7 +2836,7 @@ function processCarouselBlocks(markdown) {
2813
2836
  if (slideAttrs.image) {
2814
2837
  inner += `<img src="${escapeAttr(slideAttrs.image)}" alt="${escapeAttr(slideAttrs.title || '')}">\n`;
2815
2838
  }
2816
- const contentHtml = marked.parse(processCardBlocks(processGridBlocks(restore(slideBody.trim()))));
2839
+ const contentHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(slideBody.trim())))));
2817
2840
  if (slideAttrs.title || slideBody.trim()) {
2818
2841
  inner += `<div class="carousel-slide-content">`;
2819
2842
  if (slideAttrs.title) inner += `<h2 class="carousel-slide-title">${escapeAttr(slideAttrs.title)}</h2>`;
@@ -2915,7 +2938,7 @@ function processTimelineBlocks(markdown) {
2915
2938
  const date = ia.date ? ` data-date="${escapeAttr(ia.date)}"` : '';
2916
2939
  const status = ia.status ? ` data-status="${escapeAttr(ia.status)}"` : '';
2917
2940
  const icon = ia.icon ? ` data-icon="${escapeAttr(ia.icon)}"` : '';
2918
- const descHtml = marked.parse(processCardBlocks(processGridBlocks(restore(itemBody.trim()))));
2941
+ const descHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(itemBody.trim())))));
2919
2942
  return `<div class="dm-progression-item"${date}${status}${icon}><div class="dm-progression-item-title">${title}</div><div class="dm-progression-item-body">${descHtml}</div></div>`;
2920
2943
  }
2921
2944
  );
@@ -3257,7 +3280,7 @@ function processListGroupBlocks(markdown) {
3257
3280
  const itemClassStr = itemClasses.join(' ');
3258
3281
  let items = '';
3259
3282
  body.replace(/\[item\]([\s\S]*?)\[\/item\]/gi, (__, content) => {
3260
- const html = marked.parseInline(restore(content.trim()));
3283
+ const html = marked.parseInline(processInlineShortcodes(restore(content.trim())));
3261
3284
  items += `<div class="${itemClassStr}">${html}</div>\n`;
3262
3285
  });
3263
3286
 
@@ -3692,12 +3715,13 @@ function processHeroBlocks(markdown) {
3692
3715
  const style = styleParts.length ? ` style="${styleParts.join(';')}"` : '';
3693
3716
  const idAttr = id ? ` id="${escapeAttr(id)}"` : '';
3694
3717
 
3695
- // Button/link/cta must run before marked.parse so their quoted attributes
3696
- // aren't HTML-escaped to &quot; - that mangles parseShortcodeAttrs and
3697
- // turns `href="/x"` into a bare flag (attrs.href = true → href="true").
3698
- const processedBody = processCtaBlocks(processLinkBlocks(processButtonBlocks(
3699
- processTextBlocks(processBadgeBlocks(processCardBlocks(processGridBlocks(restore(body.trim())))))
3700
- )));
3718
+ // The inline tags must run before marked.parse (see
3719
+ // processInlineShortcodes) AND before the container tags, because
3720
+ // processCardBlocks hands the card body straight to marked - which is
3721
+ // what used to escape the attributes of anything left inside a card.
3722
+ const processedBody = processCardBlocks(processGridBlocks(
3723
+ processInlineShortcodes(restore(body.trim()))
3724
+ ));
3701
3725
 
3702
3726
  let inner = '<div class="hero-content">';
3703
3727
  if (title) inner += `<h1 class="hero-title hero-title-responsive">${escapeAttr(title)}</h1>`;
@@ -3797,7 +3821,7 @@ function processSlideoverBlocks(markdown) {
3797
3821
  const trigger = escapeAttr(attrs.trigger || 'Open');
3798
3822
  const size = escapeAttr(attrs.size || 'md');
3799
3823
  const position = escapeAttr(attrs.position || 'right');
3800
- const bodyHtml = marked.parse(processCardBlocks(processGridBlocks(restore(body.trim()))));
3824
+ const bodyHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(body.trim())))));
3801
3825
  return (
3802
3826
  `<button class="btn btn-link dm-so-trigger" data-so-target="${id}">${trigger}</button>\n` +
3803
3827
  `<div class="dm-so-content" id="${id}" style="display:none" ` +