domma-cms 0.46.4 → 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/admin/css/admin.css +1 -1
- package/admin/js/lib/component-inserter.js +1 -0
- package/admin/js/lib/markdown-toolbar.js +9 -45
- package/admin/js/lib/shortcode-context-menu.js +2 -8
- package/admin/js/lib/shortcode-form.js +8 -0
- package/admin/js/lib/shortcode-insert.js +1 -0
- package/admin/js/lib/shortcode-lookups.js +1 -0
- package/admin/js/lib/shortcode-modal.js +1 -1
- package/admin/js/lib/shortcode-preview.js +1 -0
- package/admin/js/lib/shortcode-registry.js +15 -15
- package/admin/js/lib/shortcode-tags.js +1 -1
- package/admin/js/views/page-editor.js +26 -44
- package/package.json +1 -1
- package/public/js/text-style.mjs +1 -0
- package/server/services/markdown.js +61 -118
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const SIZE_MAP={xs:".75rem",sm:".875rem",base:"1rem",lg:"1.125rem",xl:"1.25rem","2xl":"1.5rem","3xl":"1.875rem","4xl":"2.25rem"},WEIGHT_MAP={thin:"100",light:"300",normal:"400",medium:"500",semibold:"600",bold:"700",extrabold:"800",black:"900"},SPACING_MAP={tight:"-0.05em",normal:"0em",wide:"0.05em",wider:"0.1em"},COLOUR_TOKENS={primary:"var(--dm-color-primary)",secondary:"var(--dm-color-secondary)",muted:"var(--dm-text-muted)",danger:"var(--dm-color-danger)",success:"var(--dm-color-success)",warning:"var(--dm-color-warning)",info:"var(--dm-color-info)"},FONT_MAP={Georgia:"Georgia,serif",Arial:"Arial,sans-serif",Verdana:"Verdana,sans-serif","Courier New":"'Courier New',monospace","Times New Roman":"'Times New Roman',serif","Trebuchet MS":"'Trebuchet MS',sans-serif"},TRANSFORM_MAP={upper:"uppercase",lower:"lowercase",capitalize:"capitalize",none:"none"},DECORATION_MAP={underline:"underline","line-through":"line-through",none:"none"};function l(e){return e!=null&&e!==!1&&e!=="false"&&e!=="0"}function i(e){return String(e).replace(/["<>]/g,"").trim()}export function buildTextStyle(e={}){const o=[];if(e.size&&o.push(`font-size:${SIZE_MAP[e.size]||i(e.size)}`),e["point-size"]){const r=parseFloat(e["point-size"]);!isNaN(r)&&r>0&&o.push(`font-size:${r}pt`)}l(e.bold)?o.push("font-weight:700"):e.weight&&WEIGHT_MAP[e.weight]&&o.push(`font-weight:${WEIGHT_MAP[e.weight]}`),l(e.italic)&&o.push("font-style:italic");const n=e.colour||e.color;return n&&o.push(`color:${COLOUR_TOKENS[n]||i(n)}`),e.font&&FONT_MAP[e.font]&&o.push(`font-family:${FONT_MAP[e.font]}`),e.transform&&TRANSFORM_MAP[e.transform]&&o.push(`text-transform:${TRANSFORM_MAP[e.transform]}`),e.decoration&&DECORATION_MAP[e.decoration]&&o.push(`text-decoration:${DECORATION_MAP[e.decoration]}`),e.spacing&&o.push(`letter-spacing:${SPACING_MAP[e.spacing]||i(e.spacing)}`),e.align&&(o.push("display:block"),o.push(`text-align:${i(e.align)}`)),e.style&&o.push(i(e.style).replace(/;+$/,"")),o.join(";")}
|
|
@@ -15,6 +15,7 @@ import {getMenu, resolveLocation, resolveMenuDecorations} from './menus.js';
|
|
|
15
15
|
import {buildMenuNav} from './menuRender.js';
|
|
16
16
|
import {checkVisibility} from '../middleware/auth.js';
|
|
17
17
|
import {getRoleHierarchy} from './roles.js';
|
|
18
|
+
import {buildTextStyle} from '../../public/js/text-style.mjs';
|
|
18
19
|
|
|
19
20
|
const __dirname_md = path.dirname(fileURLToPath(import.meta.url));
|
|
20
21
|
const BLOCKS_DIR = path.resolve(__dirname_md, '../../content/blocks');
|
|
@@ -1667,7 +1668,7 @@ function processEffectsBlocks(markdown) {
|
|
|
1667
1668
|
if (body === null) return `<div class="${classes.join(' ')}"></div>`;
|
|
1668
1669
|
// Restore code placeholders before the nested parse (tabs-handler
|
|
1669
1670
|
// pattern) so code spans/fences render as <code>, not raw backticks.
|
|
1670
|
-
const innerHtml = marked.parse(processCardBlocks(processGridBlocks(restore(body.trim()))));
|
|
1671
|
+
const innerHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(body.trim())))));
|
|
1671
1672
|
return `<div class="${classes.join(' ')}">${innerHtml}</div>\n`;
|
|
1672
1673
|
});
|
|
1673
1674
|
|
|
@@ -1684,7 +1685,7 @@ function processEffectsBlocks(markdown) {
|
|
|
1684
1685
|
if (!hasActions) {
|
|
1685
1686
|
// Restore code placeholders before the nested parse (tabs-handler
|
|
1686
1687
|
// pattern) so code spans/fences render as <code>, not raw backticks.
|
|
1687
|
-
const innerHtml = marked.parse(processCardBlocks(processGridBlocks(restore(body.trim()))));
|
|
1688
|
+
const innerHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(body.trim())))));
|
|
1688
1689
|
return `<div class="dm-fx-scribe"${dataAttrs}>${innerHtml}</div>\n`;
|
|
1689
1690
|
}
|
|
1690
1691
|
const actions = [];
|
|
@@ -1723,7 +1724,7 @@ function processEffectsBlocks(markdown) {
|
|
|
1723
1724
|
const dataAttrs = Object.entries(attrs).map(([k, v]) => ` data-fx-${k}="${v}"`).join('');
|
|
1724
1725
|
// Restore code placeholders before the nested parse (tabs-handler
|
|
1725
1726
|
// pattern) so code spans/fences render as <code>, not raw backticks.
|
|
1726
|
-
const innerHtml = marked.parse(processCardBlocks(processGridBlocks(restore(body.trim()))));
|
|
1727
|
+
const innerHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(body.trim())))));
|
|
1727
1728
|
return `<div class="dm-fx-${name}"${dataAttrs}>${innerHtml}</div>\n`;
|
|
1728
1729
|
});
|
|
1729
1730
|
}
|
|
@@ -1740,7 +1741,7 @@ function processEffectsBlocks(markdown) {
|
|
|
1740
1741
|
}
|
|
1741
1742
|
// Restore code placeholders before the nested parse (tabs-handler
|
|
1742
1743
|
// pattern) so code spans/fences render as <code>, not raw backticks.
|
|
1743
|
-
const innerHtml = marked.parse(processCardBlocks(processGridBlocks(restore(body.trim()))));
|
|
1744
|
+
const innerHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(body.trim())))));
|
|
1744
1745
|
return `<div class="dm-fx-ticker-tape" data-fx-mode="container"${dataAttrs}>${innerHtml}</div>\n`;
|
|
1745
1746
|
});
|
|
1746
1747
|
|
|
@@ -1758,7 +1759,7 @@ function processEffectsBlocks(markdown) {
|
|
|
1758
1759
|
}
|
|
1759
1760
|
// Restore code placeholders before the nested parse (tabs-handler
|
|
1760
1761
|
// pattern) so code spans/fences render as <code>, not raw backticks.
|
|
1761
|
-
const innerHtml = marked.parse(processCardBlocks(processGridBlocks(restore(body.trim()))));
|
|
1762
|
+
const innerHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(body.trim())))));
|
|
1762
1763
|
return `<div class="dm-fx-${name}" data-fx-mode="container"${dataAttrs}>${innerHtml}</div>\n`;
|
|
1763
1764
|
});
|
|
1764
1765
|
}
|
|
@@ -1771,7 +1772,7 @@ function processEffectsBlocks(markdown) {
|
|
|
1771
1772
|
if (attrs.duration) classes.push(`animate-duration-${attrs.duration}`);
|
|
1772
1773
|
if (attrs.delay) classes.push(`animate-delay-${attrs.delay}`);
|
|
1773
1774
|
if (attrs.repeat) classes.push(`animate-${attrs.repeat}`);
|
|
1774
|
-
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`;
|
|
1775
1776
|
});
|
|
1776
1777
|
|
|
1777
1778
|
apply('ambient', (attrStr, body) => {
|
|
@@ -1781,7 +1782,7 @@ function processEffectsBlocks(markdown) {
|
|
|
1781
1782
|
if (attrs.type) classes.push(`bg-ambient-${attrs.type}`);
|
|
1782
1783
|
if (attrs.speed) classes.push(`bg-ambient-${attrs.speed}`);
|
|
1783
1784
|
if (attrs.intensity) classes.push(`bg-ambient-${attrs.intensity}`);
|
|
1784
|
-
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`;
|
|
1785
1786
|
});
|
|
1786
1787
|
|
|
1787
1788
|
return restore(result);
|
|
@@ -1847,7 +1848,7 @@ function processGridBlocks(markdown) {
|
|
|
1847
1848
|
// processCardBlocks, which creates its own empty store and fails to
|
|
1848
1849
|
// decode them, producing the literal string "undefined" in place of
|
|
1849
1850
|
// whatever was in <pre>, ``` fences, or `inline code`.
|
|
1850
|
-
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>`;
|
|
1851
1852
|
}
|
|
1852
1853
|
);
|
|
1853
1854
|
|
|
@@ -2147,7 +2148,7 @@ LAYOUT_RENDERERS['basic'] = (attrs, body) => {
|
|
|
2147
2148
|
LAYOUT_RENDERERS['header-body'] = (attrs, body) => {
|
|
2148
2149
|
const title = typeof attrs.title === 'string' ? attrs.title.trim() : '';
|
|
2149
2150
|
const hdr = title ? `<div class="card-header"><div class="card-title">${escapeAttr(title)}</div></div>` : '';
|
|
2150
|
-
const bodyHtml = marked.parse(processGridBlocks(body.trim()));
|
|
2151
|
+
const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
|
|
2151
2152
|
return `${cardRoot(attrs)}${hdr}<div class="card-body">${bodyHtml}</div>${cardFooterAttr(attrs)}</div>\n`;
|
|
2152
2153
|
};
|
|
2153
2154
|
|
|
@@ -2155,14 +2156,14 @@ LAYOUT_RENDERERS['header-body-footer'] = (attrs, body) => {
|
|
|
2155
2156
|
const title = typeof attrs.title === 'string' ? attrs.title.trim() : '';
|
|
2156
2157
|
const footer = typeof attrs.footer === 'string' ? attrs.footer.trim() : '';
|
|
2157
2158
|
const hdr = title ? `<div class="card-header"><div class="card-title">${escapeAttr(title)}</div></div>` : '';
|
|
2158
|
-
const bodyHtml = marked.parse(processGridBlocks(body.trim()));
|
|
2159
|
+
const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
|
|
2159
2160
|
const ftr = footer ? `<div class="card-footer">${escapeAttr(footer)}</div>` : '';
|
|
2160
2161
|
return `${cardRoot(attrs)}${hdr}<div class="card-body">${bodyHtml}</div>${ftr}</div>\n`;
|
|
2161
2162
|
};
|
|
2162
2163
|
|
|
2163
2164
|
LAYOUT_RENDERERS['no-header-footer'] = (attrs, body) => {
|
|
2164
2165
|
const footer = typeof attrs.footer === 'string' ? attrs.footer.trim() : '';
|
|
2165
|
-
const bodyHtml = marked.parse(processGridBlocks(body.trim()));
|
|
2166
|
+
const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
|
|
2166
2167
|
const ftr = footer ? `<div class="card-footer">${escapeAttr(footer)}</div>` : '';
|
|
2167
2168
|
return `${cardRoot(attrs)}<div class="card-body">${bodyHtml}</div>${ftr}</div>\n`;
|
|
2168
2169
|
};
|
|
@@ -2173,7 +2174,7 @@ LAYOUT_RENDERERS['icon-top'] = (attrs, body) => {
|
|
|
2173
2174
|
const icon = typeof attrs.icon === 'string' ? attrs.icon.trim() : '';
|
|
2174
2175
|
const iconHtml = icon ? `<span data-icon="${escapeAttr(icon)}" class="card-icon"></span>` : '';
|
|
2175
2176
|
const subHtml = subtitle ? `<div class="card-subtitle">${escapeAttr(subtitle)}</div>` : '';
|
|
2176
|
-
const bodyHtml = marked.parse(processGridBlocks(body.trim()));
|
|
2177
|
+
const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
|
|
2177
2178
|
return `${cardRoot(attrs)}
|
|
2178
2179
|
<div class="card-header card-header-icon-stacked">
|
|
2179
2180
|
<div class="card-avatar-wrap">${iconHtml}<div class="card-title">${escapeAttr(title)}</div>${subHtml}</div>
|
|
@@ -2187,7 +2188,7 @@ LAYOUT_RENDERERS['icon-inline'] = (attrs, body) => {
|
|
|
2187
2188
|
const icon = typeof attrs.icon === 'string' ? attrs.icon.trim() : '';
|
|
2188
2189
|
const iconHtml = icon ? `<span data-icon="${escapeAttr(icon)}"></span>` : '';
|
|
2189
2190
|
const subHtml = subtitle ? `<div class="card-subtitle">${escapeAttr(subtitle)}</div>` : '';
|
|
2190
|
-
const bodyHtml = marked.parse(processGridBlocks(body.trim()));
|
|
2191
|
+
const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
|
|
2191
2192
|
return `${cardRoot(attrs)}
|
|
2192
2193
|
<div class="card-header card-header-icon-inline">
|
|
2193
2194
|
${iconHtml}<div class="card-header-content"><div class="card-title">${escapeAttr(title)}</div>${subHtml}</div>
|
|
@@ -2198,13 +2199,13 @@ LAYOUT_RENDERERS['icon-inline'] = (attrs, body) => {
|
|
|
2198
2199
|
LAYOUT_RENDERERS['image-top'] = (attrs, body) => {
|
|
2199
2200
|
const title = typeof attrs.title === 'string' ? attrs.title.trim() : '';
|
|
2200
2201
|
const hdr = title ? `<div class="card-header"><div class="card-title">${escapeAttr(title)}</div></div>` : '';
|
|
2201
|
-
const bodyHtml = marked.parse(processGridBlocks(body.trim()));
|
|
2202
|
+
const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
|
|
2202
2203
|
return `${cardRoot(attrs)}<div class="card-img-top"${imgStyle(attrs)}></div>${hdr}<div class="card-body">${bodyHtml}</div>${cardFooterAttr(attrs)}</div>\n`;
|
|
2203
2204
|
};
|
|
2204
2205
|
|
|
2205
2206
|
LAYOUT_RENDERERS['image-overlay'] = (attrs, body) => {
|
|
2206
2207
|
const title = typeof attrs.title === 'string' ? attrs.title.trim() : '';
|
|
2207
|
-
const bodyHtml = marked.parse(processGridBlocks(body.trim()));
|
|
2208
|
+
const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
|
|
2208
2209
|
return `${cardRoot(attrs)}
|
|
2209
2210
|
<div class="card-img-overlay"${imgStyle(attrs)}>
|
|
2210
2211
|
<div class="card-overlay-text"><div class="card-title">${escapeAttr(title)}</div></div>
|
|
@@ -2214,14 +2215,14 @@ LAYOUT_RENDERERS['image-overlay'] = (attrs, body) => {
|
|
|
2214
2215
|
|
|
2215
2216
|
LAYOUT_RENDERERS['thumb-left'] = (attrs, body) => {
|
|
2216
2217
|
const title = typeof attrs.title === 'string' ? attrs.title.trim() : '';
|
|
2217
|
-
const bodyHtml = marked.parse(processGridBlocks(body.trim()));
|
|
2218
|
+
const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
|
|
2218
2219
|
return `${cardRoot(attrs, ['card-layout-thumb-left'])}<div class="card-img-left"${imgStyle(attrs)}></div>
|
|
2219
2220
|
<div class="card-body">${title ? `<div class="card-title">${escapeAttr(title)}</div>` : ''}${bodyHtml}</div>${cardFooterAttr(attrs)}</div>\n`;
|
|
2220
2221
|
};
|
|
2221
2222
|
|
|
2222
2223
|
LAYOUT_RENDERERS['thumb-right'] = (attrs, body) => {
|
|
2223
2224
|
const title = typeof attrs.title === 'string' ? attrs.title.trim() : '';
|
|
2224
|
-
const bodyHtml = marked.parse(processGridBlocks(body.trim()));
|
|
2225
|
+
const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
|
|
2225
2226
|
return `${cardRoot(attrs, ['card-layout-thumb-right'])}
|
|
2226
2227
|
<div class="card-body">${title ? `<div class="card-title">${escapeAttr(title)}</div>` : ''}${bodyHtml}</div>
|
|
2227
2228
|
<div class="card-img-right"${imgStyle(attrs)}></div></div>\n`;
|
|
@@ -2229,14 +2230,14 @@ LAYOUT_RENDERERS['thumb-right'] = (attrs, body) => {
|
|
|
2229
2230
|
|
|
2230
2231
|
LAYOUT_RENDERERS['wide-left-image'] = (attrs, body) => {
|
|
2231
2232
|
const title = typeof attrs.title === 'string' ? attrs.title.trim() : '';
|
|
2232
|
-
const bodyHtml = marked.parse(processGridBlocks(body.trim()));
|
|
2233
|
+
const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
|
|
2233
2234
|
return `${cardRoot(attrs, ['card-layout-horizontal'])}<div class="card-img-wide"${imgStyle(attrs)}></div>
|
|
2234
2235
|
<div class="card-body">${title ? `<div class="card-title">${escapeAttr(title)}</div>` : ''}${bodyHtml}${cardFooterAttr(attrs)}</div></div>\n`;
|
|
2235
2236
|
};
|
|
2236
2237
|
|
|
2237
2238
|
LAYOUT_RENDERERS['full-bg'] = (attrs, body) => {
|
|
2238
2239
|
const title = typeof attrs.title === 'string' ? attrs.title.trim() : '';
|
|
2239
|
-
const bodyHtml = marked.parse(processGridBlocks(body.trim()));
|
|
2240
|
+
const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
|
|
2240
2241
|
return `${cardRoot(attrs, ['dm-card-full-bg'])}<div class="card-img-top"${imgStyle(attrs)}></div>
|
|
2241
2242
|
<div class="card-body">${title ? `<div class="card-title">${escapeAttr(title)}</div>` : ''}${bodyHtml}</div>${cardFooterAttr(attrs)}</div>\n`;
|
|
2242
2243
|
};
|
|
@@ -2244,7 +2245,7 @@ LAYOUT_RENDERERS['full-bg'] = (attrs, body) => {
|
|
|
2244
2245
|
LAYOUT_RENDERERS['split-half'] = (attrs, body) => {
|
|
2245
2246
|
const title = typeof attrs.title === 'string' ? attrs.title.trim() : '';
|
|
2246
2247
|
const gradient = typeof attrs.gradient === 'string' ? attrs.gradient.trim() : 'indigo';
|
|
2247
|
-
const bodyHtml = marked.parse(processGridBlocks(body.trim()));
|
|
2248
|
+
const bodyHtml = marked.parse(processGridBlocks(processInlineShortcodes(body.trim())));
|
|
2248
2249
|
return `${cardRoot(attrs, ['card-layout-split'])}<div class="card-split-left card-gradient-${escapeAttr(gradient)}"${imgStyle(attrs)}></div>
|
|
2249
2250
|
<div class="card-split-right"><div class="card-body">${title ? `<div class="card-title">${escapeAttr(title)}</div>` : ''}${bodyHtml}</div></div></div>\n`;
|
|
2250
2251
|
};
|
|
@@ -2627,6 +2628,29 @@ function renderLegacyCard(attrs, body, markedInstance, escAttr) {
|
|
|
2627
2628
|
* @param {string} markdown
|
|
2628
2629
|
* @returns {string}
|
|
2629
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="x"` 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
|
+
|
|
2630
2654
|
function processCardBlocks(markdown) {
|
|
2631
2655
|
const {scrubbed, restore} = scrubCodeRegions(markdown);
|
|
2632
2656
|
return restore(scrubbed.replace(
|
|
@@ -2693,7 +2717,7 @@ function processTabsBlocks(markdown) {
|
|
|
2693
2717
|
// so fenced code blocks render as <pre><code> rather than surviving as raw text
|
|
2694
2718
|
// that would confuse subsequent scrubCodeRegions calls.
|
|
2695
2719
|
const restoredBody = restore(tabBody.trim());
|
|
2696
|
-
const bodyHtml = marked.parse(processCardBlocks(processGridBlocks(restoredBody)));
|
|
2720
|
+
const bodyHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restoredBody))));
|
|
2697
2721
|
const icon = tabAttrs.icon ? tabAttrs.icon.trim() : '';
|
|
2698
2722
|
items.push({title, icon, paneId, bodyHtml, first: tabIdx === 1});
|
|
2699
2723
|
});
|
|
@@ -2748,7 +2772,7 @@ function processAccordionBlocks(markdown) {
|
|
|
2748
2772
|
body.replace(/\[item([^\]]*)\]([\s\S]*?)\[\/item\]/gi, (__, itemAttr, itemBody) => {
|
|
2749
2773
|
const itemAttrs = parseShortcodeAttrs(itemAttr);
|
|
2750
2774
|
const title = itemAttrs.title || 'Item';
|
|
2751
|
-
const bodyHtml = marked.parse(processCardBlocks(processGridBlocks(restore(itemBody.trim()))));
|
|
2775
|
+
const bodyHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(itemBody.trim())))));
|
|
2752
2776
|
items += (
|
|
2753
2777
|
`<div class="accordion-item">\n` +
|
|
2754
2778
|
` <h3 class="accordion-header"><button class="accordion-button" type="button">${escapeAttr(title)}` +
|
|
@@ -2812,7 +2836,7 @@ function processCarouselBlocks(markdown) {
|
|
|
2812
2836
|
if (slideAttrs.image) {
|
|
2813
2837
|
inner += `<img src="${escapeAttr(slideAttrs.image)}" alt="${escapeAttr(slideAttrs.title || '')}">\n`;
|
|
2814
2838
|
}
|
|
2815
|
-
const contentHtml = marked.parse(processCardBlocks(processGridBlocks(restore(slideBody.trim()))));
|
|
2839
|
+
const contentHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(slideBody.trim())))));
|
|
2816
2840
|
if (slideAttrs.title || slideBody.trim()) {
|
|
2817
2841
|
inner += `<div class="carousel-slide-content">`;
|
|
2818
2842
|
if (slideAttrs.title) inner += `<h2 class="carousel-slide-title">${escapeAttr(slideAttrs.title)}</h2>`;
|
|
@@ -2914,7 +2938,7 @@ function processTimelineBlocks(markdown) {
|
|
|
2914
2938
|
const date = ia.date ? ` data-date="${escapeAttr(ia.date)}"` : '';
|
|
2915
2939
|
const status = ia.status ? ` data-status="${escapeAttr(ia.status)}"` : '';
|
|
2916
2940
|
const icon = ia.icon ? ` data-icon="${escapeAttr(ia.icon)}"` : '';
|
|
2917
|
-
const descHtml = marked.parse(processCardBlocks(processGridBlocks(restore(itemBody.trim()))));
|
|
2941
|
+
const descHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(itemBody.trim())))));
|
|
2918
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>`;
|
|
2919
2943
|
}
|
|
2920
2944
|
);
|
|
@@ -3014,94 +3038,12 @@ function processBadgeBlocks(markdown) {
|
|
|
3014
3038
|
function processTextBlocks(markdown) {
|
|
3015
3039
|
const {scrubbed, restore} = scrubCodeRegions(markdown);
|
|
3016
3040
|
|
|
3017
|
-
const SIZE_MAP = {
|
|
3018
|
-
xs: '.75rem', sm: '.875rem', base: '1rem', lg: '1.125rem',
|
|
3019
|
-
xl: '1.25rem', '2xl': '1.5rem', '3xl': '1.875rem', '4xl': '2.25rem',
|
|
3020
|
-
};
|
|
3021
|
-
const WEIGHT_MAP = {
|
|
3022
|
-
thin: '100', light: '300', normal: '400', medium: '500',
|
|
3023
|
-
semibold: '600', bold: '700', extrabold: '800', black: '900',
|
|
3024
|
-
};
|
|
3025
|
-
const SPACING_MAP = {
|
|
3026
|
-
tight: '-0.05em', normal: '0em', wide: '0.05em', wider: '0.1em',
|
|
3027
|
-
};
|
|
3028
|
-
const COLOR_TOKENS = {
|
|
3029
|
-
primary: 'var(--dm-color-primary)', secondary: 'var(--dm-color-secondary)',
|
|
3030
|
-
muted: 'var(--dm-text-muted)', danger: 'var(--dm-color-danger)',
|
|
3031
|
-
success: 'var(--dm-color-success)', warning: 'var(--dm-color-warning)',
|
|
3032
|
-
info: 'var(--dm-color-info)',
|
|
3033
|
-
};
|
|
3034
|
-
const FONT_MAP = {
|
|
3035
|
-
'Georgia': 'Georgia,serif',
|
|
3036
|
-
'Arial': 'Arial,sans-serif',
|
|
3037
|
-
'Verdana': 'Verdana,sans-serif',
|
|
3038
|
-
'Courier New': "'Courier New',monospace",
|
|
3039
|
-
'Times New Roman': "'Times New Roman',serif",
|
|
3040
|
-
'Trebuchet MS': "'Trebuchet MS',sans-serif",
|
|
3041
|
-
};
|
|
3042
|
-
const TRANSFORM_MAP = {
|
|
3043
|
-
upper: 'uppercase', lower: 'lowercase', capitalize: 'capitalize', none: 'none',
|
|
3044
|
-
};
|
|
3045
|
-
const DECORATION_MAP = {
|
|
3046
|
-
underline: 'underline', 'line-through': 'line-through', none: 'none',
|
|
3047
|
-
};
|
|
3048
|
-
|
|
3049
3041
|
function buildText(attrStr, inner) {
|
|
3050
3042
|
const attrs = parseShortcodeAttrs(attrStr || '');
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
}
|
|
3056
|
-
|
|
3057
|
-
if (attrs['point-size']) {
|
|
3058
|
-
const pt = parseFloat(attrs['point-size']);
|
|
3059
|
-
if (!isNaN(pt) && pt > 0) styles.push(`font-size:${pt}pt`);
|
|
3060
|
-
}
|
|
3061
|
-
|
|
3062
|
-
// bold flag takes precedence over weight attr
|
|
3063
|
-
const isBold = /\bbold\b/i.test(attrStr);
|
|
3064
|
-
if (isBold) {
|
|
3065
|
-
styles.push('font-weight:700');
|
|
3066
|
-
} else if (attrs.weight && WEIGHT_MAP[attrs.weight]) {
|
|
3067
|
-
styles.push(`font-weight:${WEIGHT_MAP[attrs.weight]}`);
|
|
3068
|
-
}
|
|
3069
|
-
|
|
3070
|
-
if (/\bitalic\b/i.test(attrStr)) {
|
|
3071
|
-
styles.push('font-style:italic');
|
|
3072
|
-
}
|
|
3073
|
-
|
|
3074
|
-
const colourVal = attrs.colour || attrs.color;
|
|
3075
|
-
if (colourVal) {
|
|
3076
|
-
styles.push(`color:${COLOR_TOKENS[colourVal] || colourVal}`);
|
|
3077
|
-
}
|
|
3078
|
-
|
|
3079
|
-
if (attrs.font && FONT_MAP[attrs.font]) {
|
|
3080
|
-
styles.push(`font-family:${FONT_MAP[attrs.font]}`);
|
|
3081
|
-
}
|
|
3082
|
-
|
|
3083
|
-
if (attrs.transform && TRANSFORM_MAP[attrs.transform]) {
|
|
3084
|
-
styles.push(`text-transform:${TRANSFORM_MAP[attrs.transform]}`);
|
|
3085
|
-
}
|
|
3086
|
-
|
|
3087
|
-
if (attrs.decoration && DECORATION_MAP[attrs.decoration]) {
|
|
3088
|
-
styles.push(`text-decoration:${DECORATION_MAP[attrs.decoration]}`);
|
|
3089
|
-
}
|
|
3090
|
-
|
|
3091
|
-
if (attrs.spacing) {
|
|
3092
|
-
styles.push(`letter-spacing:${SPACING_MAP[attrs.spacing] || attrs.spacing}`);
|
|
3093
|
-
}
|
|
3094
|
-
|
|
3095
|
-
if (attrs.align) {
|
|
3096
|
-
styles.push('display:block');
|
|
3097
|
-
styles.push(`text-align:${escapeAttr(attrs.align)}`);
|
|
3098
|
-
}
|
|
3099
|
-
|
|
3100
|
-
if (attrs.style) {
|
|
3101
|
-
styles.push(attrs.style.trim().replace(/;+$/, ''));
|
|
3102
|
-
}
|
|
3103
|
-
|
|
3104
|
-
const styleAttr = styles.length ? ` style="${styles.join(';')}"` : '';
|
|
3043
|
+
// The attribute → CSS mapping is shared with the admin editor's live
|
|
3044
|
+
// preview (public/js/text-style.mjs), so the two cannot disagree.
|
|
3045
|
+
const style = buildTextStyle(attrs);
|
|
3046
|
+
const styleAttr = style ? ` style="${style}"` : '';
|
|
3105
3047
|
const classAttr = attrs.class ? ` class="${escapeAttr(attrs.class)}"` : '';
|
|
3106
3048
|
const idAttr = attrs.id ? ` id="${escapeAttr(attrs.id)}"` : '';
|
|
3107
3049
|
|
|
@@ -3338,7 +3280,7 @@ function processListGroupBlocks(markdown) {
|
|
|
3338
3280
|
const itemClassStr = itemClasses.join(' ');
|
|
3339
3281
|
let items = '';
|
|
3340
3282
|
body.replace(/\[item\]([\s\S]*?)\[\/item\]/gi, (__, content) => {
|
|
3341
|
-
const html = marked.parseInline(restore(content.trim()));
|
|
3283
|
+
const html = marked.parseInline(processInlineShortcodes(restore(content.trim())));
|
|
3342
3284
|
items += `<div class="${itemClassStr}">${html}</div>\n`;
|
|
3343
3285
|
});
|
|
3344
3286
|
|
|
@@ -3773,12 +3715,13 @@ function processHeroBlocks(markdown) {
|
|
|
3773
3715
|
const style = styleParts.length ? ` style="${styleParts.join(';')}"` : '';
|
|
3774
3716
|
const idAttr = id ? ` id="${escapeAttr(id)}"` : '';
|
|
3775
3717
|
|
|
3776
|
-
//
|
|
3777
|
-
//
|
|
3778
|
-
//
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
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
|
+
));
|
|
3782
3725
|
|
|
3783
3726
|
let inner = '<div class="hero-content">';
|
|
3784
3727
|
if (title) inner += `<h1 class="hero-title hero-title-responsive">${escapeAttr(title)}</h1>`;
|
|
@@ -3878,7 +3821,7 @@ function processSlideoverBlocks(markdown) {
|
|
|
3878
3821
|
const trigger = escapeAttr(attrs.trigger || 'Open');
|
|
3879
3822
|
const size = escapeAttr(attrs.size || 'md');
|
|
3880
3823
|
const position = escapeAttr(attrs.position || 'right');
|
|
3881
|
-
const bodyHtml = marked.parse(processCardBlocks(processGridBlocks(restore(body.trim()))));
|
|
3824
|
+
const bodyHtml = marked.parse(processCardBlocks(processGridBlocks(processInlineShortcodes(restore(body.trim())))));
|
|
3882
3825
|
return (
|
|
3883
3826
|
`<button class="btn btn-link dm-so-trigger" data-so-target="${id}">${trigger}</button>\n` +
|
|
3884
3827
|
`<div class="dm-so-content" id="${id}" style="display:none" ` +
|