@pie-lib/render-ui 5.2.0-next.8 → 6.0.0

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/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [5.2.0-next.9](https://github.com/pie-framework/pie-lib/compare/@pie-lib/render-ui@5.2.0-next.8...@pie-lib/render-ui@5.2.0-next.9) (2026-04-23)
7
+
8
+ ### Features
9
+
10
+ - **render-ui:** add transformDataHeadings function to convert data-heading paragraphs to headings - PIE-151 ([b59a7cc](https://github.com/pie-framework/pie-lib/commit/b59a7cc091ce9d1ace1679bd854e72ab3d57fc76))
11
+
6
12
  # [5.2.0-next.8](https://github.com/pie-framework/pie-lib/compare/@pie-lib/render-ui@5.2.0-next.7...@pie-lib/render-ui@5.2.0-next.8) (2026-04-15)
7
13
 
8
14
  ### Bug Fixes
package/lib/index.js CHANGED
@@ -79,6 +79,12 @@ Object.defineProperty(exports, "hasText", {
79
79
  }
80
80
  });
81
81
  exports.indicators = void 0;
82
+ Object.defineProperty(exports, "transformDataHeadings", {
83
+ enumerable: true,
84
+ get: function get() {
85
+ return _transformHeadings.transformDataHeadings;
86
+ }
87
+ });
82
88
  Object.defineProperty(exports, "withUndoReset", {
83
89
  enumerable: true,
84
90
  get: function get() {
@@ -102,5 +108,6 @@ exports.color = color;
102
108
  var _hasText = require("./has-text");
103
109
  var _hasMedia = require("./has-media");
104
110
  var _enableAudioAutoplayImage = _interopRequireDefault(require("./assets/enableAudioAutoplayImage"));
111
+ var _transformHeadings = require("./transform-headings");
105
112
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
106
113
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["indicators","_interopRequireWildcard","require","exports","_feedback","_interopRequireDefault","_collapsible","_withUndoReset","_previewLayout","_uiLayout","_htmlAndMath","_inputContainer","_previewPrompt","_readable","_purpose","color","_hasText","_hasMedia","_enableAudioAutoplayImage","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","_typeof","has","get","set","_t","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor"],"sources":["../src/index.js"],"sourcesContent":["import * as indicators from './response-indicators';\nimport Feedback from './feedback';\nimport Collapsible from './collapsible';\nimport withUndoReset from './withUndoReset';\nimport PreviewLayout from './preview-layout';\nimport UiLayout from './ui-layout';\nimport HtmlAndMath from './html-and-math';\nimport InputContainer from './input-container';\nimport PreviewPrompt from './preview-prompt';\nimport Readable from './readable';\nimport Purpose from './purpose';\nimport * as color from './color';\nimport { hasText } from './has-text';\nimport { hasMedia } from './has-media';\nimport EnableAudioAutoplayImage from './assets/enableAudioAutoplayImage';\n\nexport {\n HtmlAndMath,\n indicators,\n withUndoReset,\n Feedback,\n UiLayout,\n PreviewLayout,\n Collapsible,\n InputContainer,\n PreviewPrompt,\n color,\n Readable,\n Purpose,\n hasText,\n hasMedia,\n EnableAudioAutoplayImage,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,UAAA,GAAAC,uBAAA,CAAAC,OAAA;AAAoDC,OAAA,CAAAH,UAAA,GAAAA,UAAA;AACpD,IAAAI,SAAA,GAAAC,sBAAA,CAAAH,OAAA;AACA,IAAAI,YAAA,GAAAD,sBAAA,CAAAH,OAAA;AACA,IAAAK,cAAA,GAAAF,sBAAA,CAAAH,OAAA;AACA,IAAAM,cAAA,GAAAH,sBAAA,CAAAH,OAAA;AACA,IAAAO,SAAA,GAAAJ,sBAAA,CAAAH,OAAA;AACA,IAAAQ,YAAA,GAAAL,sBAAA,CAAAH,OAAA;AACA,IAAAS,eAAA,GAAAN,sBAAA,CAAAH,OAAA;AACA,IAAAU,cAAA,GAAAP,sBAAA,CAAAH,OAAA;AACA,IAAAW,SAAA,GAAAR,sBAAA,CAAAH,OAAA;AACA,IAAAY,QAAA,GAAAT,sBAAA,CAAAH,OAAA;AACA,IAAAa,KAAA,GAAAd,uBAAA,CAAAC,OAAA;AAAiCC,OAAA,CAAAY,KAAA,GAAAA,KAAA;AACjC,IAAAC,QAAA,GAAAd,OAAA;AACA,IAAAe,SAAA,GAAAf,OAAA;AACA,IAAAgB,yBAAA,GAAAb,sBAAA,CAAAH,OAAA;AAAyE,SAAAD,wBAAAkB,CAAA,EAAAC,CAAA,6BAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAApB,uBAAA,YAAAA,wBAAAkB,CAAA,EAAAC,CAAA,SAAAA,CAAA,IAAAD,CAAA,IAAAA,CAAA,CAAAK,UAAA,SAAAL,CAAA,MAAAM,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,mBAAAT,CAAA,iBAAAA,CAAA,gBAAAU,OAAA,CAAAV,CAAA,0BAAAA,CAAA,SAAAQ,CAAA,MAAAF,CAAA,GAAAL,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAG,CAAA,CAAAK,GAAA,CAAAX,CAAA,UAAAM,CAAA,CAAAM,GAAA,CAAAZ,CAAA,GAAAM,CAAA,CAAAO,GAAA,CAAAb,CAAA,EAAAQ,CAAA,cAAAM,EAAA,IAAAd,CAAA,gBAAAc,EAAA,OAAAC,cAAA,CAAAC,IAAA,CAAAhB,CAAA,EAAAc,EAAA,OAAAP,CAAA,IAAAD,CAAA,GAAAW,MAAA,CAAAC,cAAA,KAAAD,MAAA,CAAAE,wBAAA,CAAAnB,CAAA,EAAAc,EAAA,OAAAP,CAAA,CAAAK,GAAA,IAAAL,CAAA,CAAAM,GAAA,IAAAP,CAAA,CAAAE,CAAA,EAAAM,EAAA,EAAAP,CAAA,IAAAC,CAAA,CAAAM,EAAA,IAAAd,CAAA,CAAAc,EAAA,WAAAN,CAAA,KAAAR,CAAA,EAAAC,CAAA","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["indicators","_interopRequireWildcard","require","exports","_feedback","_interopRequireDefault","_collapsible","_withUndoReset","_previewLayout","_uiLayout","_htmlAndMath","_inputContainer","_previewPrompt","_readable","_purpose","color","_hasText","_hasMedia","_enableAudioAutoplayImage","_transformHeadings","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","_typeof","has","get","set","_t","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor"],"sources":["../src/index.js"],"sourcesContent":["import * as indicators from './response-indicators';\nimport Feedback from './feedback';\nimport Collapsible from './collapsible';\nimport withUndoReset from './withUndoReset';\nimport PreviewLayout from './preview-layout';\nimport UiLayout from './ui-layout';\nimport HtmlAndMath from './html-and-math';\nimport InputContainer from './input-container';\nimport PreviewPrompt from './preview-prompt';\nimport Readable from './readable';\nimport Purpose from './purpose';\nimport * as color from './color';\nimport { hasText } from './has-text';\nimport { hasMedia } from './has-media';\nimport EnableAudioAutoplayImage from './assets/enableAudioAutoplayImage';\nimport { transformDataHeadings } from './transform-headings';\n\nexport {\n HtmlAndMath,\n indicators,\n withUndoReset,\n Feedback,\n UiLayout,\n PreviewLayout,\n Collapsible,\n InputContainer,\n PreviewPrompt,\n color,\n Readable,\n Purpose,\n hasText,\n hasMedia,\n EnableAudioAutoplayImage,\n transformDataHeadings,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,UAAA,GAAAC,uBAAA,CAAAC,OAAA;AAAoDC,OAAA,CAAAH,UAAA,GAAAA,UAAA;AACpD,IAAAI,SAAA,GAAAC,sBAAA,CAAAH,OAAA;AACA,IAAAI,YAAA,GAAAD,sBAAA,CAAAH,OAAA;AACA,IAAAK,cAAA,GAAAF,sBAAA,CAAAH,OAAA;AACA,IAAAM,cAAA,GAAAH,sBAAA,CAAAH,OAAA;AACA,IAAAO,SAAA,GAAAJ,sBAAA,CAAAH,OAAA;AACA,IAAAQ,YAAA,GAAAL,sBAAA,CAAAH,OAAA;AACA,IAAAS,eAAA,GAAAN,sBAAA,CAAAH,OAAA;AACA,IAAAU,cAAA,GAAAP,sBAAA,CAAAH,OAAA;AACA,IAAAW,SAAA,GAAAR,sBAAA,CAAAH,OAAA;AACA,IAAAY,QAAA,GAAAT,sBAAA,CAAAH,OAAA;AACA,IAAAa,KAAA,GAAAd,uBAAA,CAAAC,OAAA;AAAiCC,OAAA,CAAAY,KAAA,GAAAA,KAAA;AACjC,IAAAC,QAAA,GAAAd,OAAA;AACA,IAAAe,SAAA,GAAAf,OAAA;AACA,IAAAgB,yBAAA,GAAAb,sBAAA,CAAAH,OAAA;AACA,IAAAiB,kBAAA,GAAAjB,OAAA;AAA6D,SAAAD,wBAAAmB,CAAA,EAAAC,CAAA,6BAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAArB,uBAAA,YAAAA,wBAAAmB,CAAA,EAAAC,CAAA,SAAAA,CAAA,IAAAD,CAAA,IAAAA,CAAA,CAAAK,UAAA,SAAAL,CAAA,MAAAM,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,mBAAAT,CAAA,iBAAAA,CAAA,gBAAAU,OAAA,CAAAV,CAAA,0BAAAA,CAAA,SAAAQ,CAAA,MAAAF,CAAA,GAAAL,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAG,CAAA,CAAAK,GAAA,CAAAX,CAAA,UAAAM,CAAA,CAAAM,GAAA,CAAAZ,CAAA,GAAAM,CAAA,CAAAO,GAAA,CAAAb,CAAA,EAAAQ,CAAA,cAAAM,EAAA,IAAAd,CAAA,gBAAAc,EAAA,OAAAC,cAAA,CAAAC,IAAA,CAAAhB,CAAA,EAAAc,EAAA,OAAAP,CAAA,IAAAD,CAAA,GAAAW,MAAA,CAAAC,cAAA,KAAAD,MAAA,CAAAE,wBAAA,CAAAnB,CAAA,EAAAc,EAAA,OAAAP,CAAA,CAAAK,GAAA,IAAAL,CAAA,CAAAM,GAAA,IAAAP,CAAA,CAAAE,CAAA,EAAAM,EAAA,EAAAP,CAAA,IAAAC,CAAA,CAAAM,EAAA,IAAAd,CAAA,CAAAc,EAAA,WAAAN,CAAA,KAAAR,CAAA,EAAAC,CAAA","ignoreList":[]}
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.transformDataHeadings = transformDataHeadings;
7
+ /**
8
+ * transformDataHeadings
9
+ *
10
+ * Rewrites every `<p data-heading="headingN">…</p>` in an HTML string to a
11
+ * proper heading element whose level is `clamp(baseLevel + N - 1, 1, 6)`.
12
+ *
13
+ *
14
+ * This is a pure function: it performs no DOM mutations and has no side-effects.
15
+ * It relies on the browser's (or jsdom's) HTML parser via DOMParser, so it must
16
+ * only be called in an environment that provides that global.
17
+ *
18
+ * @param {string} html - Raw HTML string (passage body, prompt, etc.)
19
+ * @param {number} baseLevel - The base heading level (1–6). Defaults to 2.
20
+ * @returns {string} - HTML string with promoted heading elements.
21
+ *
22
+ * @example
23
+ * // baseLevel=2, data-heading="heading1" → <h2 data-heading="heading1">…</h2>
24
+ * // baseLevel=2, data-heading="heading2" → <h3 data-heading="heading2">…</h3>
25
+ * // baseLevel=3, data-heading="heading1" → <h3 data-heading="heading1">…</h3>
26
+ */
27
+ function transformDataHeadings() {
28
+ var html = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
29
+ var baseLevel = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2;
30
+ if (!html) return html;
31
+ var parser = new DOMParser();
32
+ var doc = parser.parseFromString(html, 'text/html');
33
+ doc.body.querySelectorAll('[data-heading]').forEach(function (el) {
34
+ var raw = el.getAttribute('data-heading') || '';
35
+ var n = parseInt(raw.replace('heading', ''), 10);
36
+ if (!Number.isFinite(n)) return; // skip malformed values
37
+
38
+ var level = Math.min(6, Math.max(1, baseLevel + n - 1));
39
+ var heading = doc.createElement("h".concat(level));
40
+ heading.setAttribute('data-heading', raw);
41
+
42
+ // Copy all other attributes (class, id, style, …)
43
+ Array.from(el.attributes).forEach(function (attr) {
44
+ if (attr.name !== 'data-heading') {
45
+ heading.setAttribute(attr.name, attr.value);
46
+ }
47
+ });
48
+ heading.innerHTML = el.innerHTML;
49
+ el.replaceWith(heading);
50
+ });
51
+ return doc.body.innerHTML;
52
+ }
53
+ //# sourceMappingURL=transform-headings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transform-headings.js","names":["transformDataHeadings","html","arguments","length","undefined","baseLevel","parser","DOMParser","doc","parseFromString","body","querySelectorAll","forEach","el","raw","getAttribute","n","parseInt","replace","Number","isFinite","level","Math","min","max","heading","createElement","concat","setAttribute","Array","from","attributes","attr","name","value","innerHTML","replaceWith"],"sources":["../src/transform-headings.js"],"sourcesContent":["/**\n * transformDataHeadings\n *\n * Rewrites every `<p data-heading=\"headingN\">…</p>` in an HTML string to a\n * proper heading element whose level is `clamp(baseLevel + N - 1, 1, 6)`.\n *\n *\n * This is a pure function: it performs no DOM mutations and has no side-effects.\n * It relies on the browser's (or jsdom's) HTML parser via DOMParser, so it must\n * only be called in an environment that provides that global.\n *\n * @param {string} html - Raw HTML string (passage body, prompt, etc.)\n * @param {number} baseLevel - The base heading level (1–6). Defaults to 2.\n * @returns {string} - HTML string with promoted heading elements.\n *\n * @example\n * // baseLevel=2, data-heading=\"heading1\" → <h2 data-heading=\"heading1\">…</h2>\n * // baseLevel=2, data-heading=\"heading2\" → <h3 data-heading=\"heading2\">…</h3>\n * // baseLevel=3, data-heading=\"heading1\" → <h3 data-heading=\"heading1\">…</h3>\n */\nexport function transformDataHeadings(html = '', baseLevel = 2) {\n if (!html) return html;\n\n const parser = new DOMParser();\n const doc = parser.parseFromString(html, 'text/html');\n\n doc.body.querySelectorAll('[data-heading]').forEach((el) => {\n const raw = el.getAttribute('data-heading') || '';\n const n = parseInt(raw.replace('heading', ''), 10);\n\n if (!Number.isFinite(n)) return; // skip malformed values\n\n const level = Math.min(6, Math.max(1, baseLevel + n - 1));\n const heading = doc.createElement(`h${level}`);\n\n heading.setAttribute('data-heading', raw);\n\n // Copy all other attributes (class, id, style, …)\n Array.from(el.attributes).forEach((attr) => {\n if (attr.name !== 'data-heading') {\n heading.setAttribute(attr.name, attr.value);\n }\n });\n\n heading.innerHTML = el.innerHTML;\n el.replaceWith(heading);\n });\n\n return doc.body.innerHTML;\n}\n"],"mappings":";;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASA,qBAAqBA,CAAA,EAA2B;EAAA,IAA1BC,IAAI,GAAAC,SAAA,CAAAC,MAAA,QAAAD,SAAA,QAAAE,SAAA,GAAAF,SAAA,MAAG,EAAE;EAAA,IAAEG,SAAS,GAAAH,SAAA,CAAAC,MAAA,QAAAD,SAAA,QAAAE,SAAA,GAAAF,SAAA,MAAG,CAAC;EAC5D,IAAI,CAACD,IAAI,EAAE,OAAOA,IAAI;EAEtB,IAAMK,MAAM,GAAG,IAAIC,SAAS,CAAC,CAAC;EAC9B,IAAMC,GAAG,GAAGF,MAAM,CAACG,eAAe,CAACR,IAAI,EAAE,WAAW,CAAC;EAErDO,GAAG,CAACE,IAAI,CAACC,gBAAgB,CAAC,gBAAgB,CAAC,CAACC,OAAO,CAAC,UAACC,EAAE,EAAK;IAC1D,IAAMC,GAAG,GAAGD,EAAE,CAACE,YAAY,CAAC,cAAc,CAAC,IAAI,EAAE;IACjD,IAAMC,CAAC,GAAGC,QAAQ,CAACH,GAAG,CAACI,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;IAElD,IAAI,CAACC,MAAM,CAACC,QAAQ,CAACJ,CAAC,CAAC,EAAE,OAAO,CAAC;;IAEjC,IAAMK,KAAK,GAAGC,IAAI,CAACC,GAAG,CAAC,CAAC,EAAED,IAAI,CAACE,GAAG,CAAC,CAAC,EAAEnB,SAAS,GAAGW,CAAC,GAAG,CAAC,CAAC,CAAC;IACzD,IAAMS,OAAO,GAAGjB,GAAG,CAACkB,aAAa,KAAAC,MAAA,CAAKN,KAAK,CAAE,CAAC;IAE9CI,OAAO,CAACG,YAAY,CAAC,cAAc,EAAEd,GAAG,CAAC;;IAEzC;IACAe,KAAK,CAACC,IAAI,CAACjB,EAAE,CAACkB,UAAU,CAAC,CAACnB,OAAO,CAAC,UAACoB,IAAI,EAAK;MAC1C,IAAIA,IAAI,CAACC,IAAI,KAAK,cAAc,EAAE;QAChCR,OAAO,CAACG,YAAY,CAACI,IAAI,CAACC,IAAI,EAAED,IAAI,CAACE,KAAK,CAAC;MAC7C;IACF,CAAC,CAAC;IAEFT,OAAO,CAACU,SAAS,GAAGtB,EAAE,CAACsB,SAAS;IAChCtB,EAAE,CAACuB,WAAW,CAACX,OAAO,CAAC;EACzB,CAAC,CAAC;EAEF,OAAOjB,GAAG,CAACE,IAAI,CAACyB,SAAS;AAC3B","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pie-lib/render-ui",
3
- "version": "5.2.0-next.8",
3
+ "version": "6.0.0",
4
4
  "description": "Some shared ui elements when rendering - but not worthy of their own package yet.",
5
5
  "module": "src/index.js",
6
6
  "main": "lib/index.js",
@@ -14,9 +14,9 @@
14
14
  "@emotion/styled": "^11.14.1",
15
15
  "@mui/icons-material": "^7.3.4",
16
16
  "@mui/material": "^7.3.4",
17
- "@pie-lib/icons": "^3.2.0-next.4",
18
- "@pie-lib/math-rendering": "^4.2.0-next.3",
19
- "@pie-lib/test-utils": "^1.2.0-next.3",
17
+ "@pie-lib/icons": "^4.0.0",
18
+ "@pie-lib/math-rendering": "^5.0.0",
19
+ "@pie-lib/test-utils": "^2.0.0",
20
20
  "debug": "^4.1.1",
21
21
  "lodash-es": "^4.17.23",
22
22
  "prop-types": "^15.7.2",
@@ -31,5 +31,5 @@
31
31
  "react": "^18.2.0",
32
32
  "react-dom": "^18.2.0"
33
33
  },
34
- "gitHead": "80a22e0d49de8a923a1130afa9532e6d2d58eadd"
34
+ "gitHead": "bf0904ba8bdd2d6f88fb8ad99060e45e40180348"
35
35
  }
package/src/index.js CHANGED
@@ -13,6 +13,7 @@ import * as color from './color';
13
13
  import { hasText } from './has-text';
14
14
  import { hasMedia } from './has-media';
15
15
  import EnableAudioAutoplayImage from './assets/enableAudioAutoplayImage';
16
+ import { transformDataHeadings } from './transform-headings';
16
17
 
17
18
  export {
18
19
  HtmlAndMath,
@@ -30,4 +31,5 @@ export {
30
31
  hasText,
31
32
  hasMedia,
32
33
  EnableAudioAutoplayImage,
34
+ transformDataHeadings,
33
35
  };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * transformDataHeadings
3
+ *
4
+ * Rewrites every `<p data-heading="headingN">…</p>` in an HTML string to a
5
+ * proper heading element whose level is `clamp(baseLevel + N - 1, 1, 6)`.
6
+ *
7
+ *
8
+ * This is a pure function: it performs no DOM mutations and has no side-effects.
9
+ * It relies on the browser's (or jsdom's) HTML parser via DOMParser, so it must
10
+ * only be called in an environment that provides that global.
11
+ *
12
+ * @param {string} html - Raw HTML string (passage body, prompt, etc.)
13
+ * @param {number} baseLevel - The base heading level (1–6). Defaults to 2.
14
+ * @returns {string} - HTML string with promoted heading elements.
15
+ *
16
+ * @example
17
+ * // baseLevel=2, data-heading="heading1" → <h2 data-heading="heading1">…</h2>
18
+ * // baseLevel=2, data-heading="heading2" → <h3 data-heading="heading2">…</h3>
19
+ * // baseLevel=3, data-heading="heading1" → <h3 data-heading="heading1">…</h3>
20
+ */
21
+ export function transformDataHeadings(html = '', baseLevel = 2) {
22
+ if (!html) return html;
23
+
24
+ const parser = new DOMParser();
25
+ const doc = parser.parseFromString(html, 'text/html');
26
+
27
+ doc.body.querySelectorAll('[data-heading]').forEach((el) => {
28
+ const raw = el.getAttribute('data-heading') || '';
29
+ const n = parseInt(raw.replace('heading', ''), 10);
30
+
31
+ if (!Number.isFinite(n)) return; // skip malformed values
32
+
33
+ const level = Math.min(6, Math.max(1, baseLevel + n - 1));
34
+ const heading = doc.createElement(`h${level}`);
35
+
36
+ heading.setAttribute('data-heading', raw);
37
+
38
+ // Copy all other attributes (class, id, style, …)
39
+ Array.from(el.attributes).forEach((attr) => {
40
+ if (attr.name !== 'data-heading') {
41
+ heading.setAttribute(attr.name, attr.value);
42
+ }
43
+ });
44
+
45
+ heading.innerHTML = el.innerHTML;
46
+ el.replaceWith(heading);
47
+ });
48
+
49
+ return doc.body.innerHTML;
50
+ }