rapier-markdown-kit 1.1.101 → 1.1.104

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.
Files changed (60) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +213 -53
  3. package/dist/agent/vendor/hyphen-en-us/LICENSE +14 -0
  4. package/dist/agent/vendor/hyphen-en-us/SOURCE.json +33 -0
  5. package/dist/agent/vendor/hyphen-en-us/hyph-en-us.hyp.txt +14 -0
  6. package/dist/agent/vendor/hyphen-en-us/hyph-en-us.pat.txt +4938 -0
  7. package/dist/agent/vendor/hyphen-en-us/patterns.js +25 -0
  8. package/dist/agent/vendor/pretext/LICENSE +208 -0
  9. package/dist/agent/vendor/pretext/NOTICE +26 -13
  10. package/dist/agent/vendor/pretext/SOURCE.json +213 -45
  11. package/dist/agent/vendor/pretext/analysis.js +675 -1224
  12. package/dist/agent/vendor/pretext/entry-geometry.js +183 -0
  13. package/dist/agent/vendor/pretext/gecko-line-breaks.js +688 -0
  14. package/dist/agent/vendor/pretext/generated/engine-break-data.js +57 -0
  15. package/dist/agent/vendor/pretext/generated/webkit-generic-families.js +80 -0
  16. package/dist/agent/vendor/pretext/graphemes.js +98 -0
  17. package/dist/agent/vendor/pretext/han-kerning.js +253 -0
  18. package/dist/agent/vendor/pretext/layout.js +352 -290
  19. package/dist/agent/vendor/pretext/line-break.js +1154 -588
  20. package/dist/agent/vendor/pretext/line-breaks.js +892 -0
  21. package/dist/agent/vendor/pretext/line-text.js +81 -39
  22. package/dist/agent/vendor/pretext/measurement.js +1105 -176
  23. package/dist/agent/vendor/pretext/prepare.js +807 -0
  24. package/dist/agent/vendor/pretext/rich-inline.js +1242 -273
  25. package/dist/agent/will.mjs +2 -0
  26. package/dist/kit/assets.mjs +5 -1
  27. package/dist/kit/conformance/run.mjs +4 -4
  28. package/dist/kit/export-fonts.mjs +26 -11
  29. package/dist/kit/index.mjs +1 -0
  30. package/dist/kit/ledger/carried.mjs +9 -7
  31. package/dist/kit/ledger/data.mjs +73 -13
  32. package/dist/kit/ledger/format.mjs +166 -53
  33. package/dist/kit/ledger/hash.mjs +75 -18
  34. package/dist/kit/ledger/history.mjs +143 -115
  35. package/dist/kit/ledger/inverse-source.mjs +158 -19
  36. package/dist/kit/ledger/journal-records.mjs +292 -67
  37. package/dist/kit/model.mjs +1 -1
  38. package/dist/kit/pdf-glyphs.mjs +4 -4
  39. package/dist/kit/render-markdown.mjs +259 -122
  40. package/dist/kit/render-sanitize.mjs +24 -4
  41. package/dist/kit/render-work.mjs +99 -20
  42. package/dist/kit/render.mjs +101 -65
  43. package/dist/kit/typography.mjs +2 -0
  44. package/dist/layout/hyphenation.mjs +127 -0
  45. package/dist/layout/line-plan.mjs +88 -96
  46. package/dist/layout/model.mjs +61 -80
  47. package/dist/layout/paragraph-fit.mjs +191 -0
  48. package/dist/layout/preparation.mjs +373 -0
  49. package/dist/layout/prepared-paragraph.mjs +223 -0
  50. package/dist/layout/typography.mjs +345 -111
  51. package/dist/packages/rapier-diff/index.mjs +288 -196
  52. package/dist/spec/document-fonts.mjs +13 -4
  53. package/dist/spec/md-assets.mjs +149 -30
  54. package/dist/spec/md-layout.mjs +5 -2
  55. package/dist/spec/md-marks.mjs +218 -3
  56. package/dist/spec/md-source.mjs +122 -0
  57. package/package.json +2 -1
  58. package/style.css +9 -0
  59. package/dist/agent/vendor/pretext/bidi.js +0 -151
  60. package/dist/agent/vendor/pretext/generated/bidi-data.js +0 -3831
@@ -1,6 +1,6 @@
1
1
  import {htmlPlaceholder, mountHtmlBlocks, htmlImageRecord, htmlLayoutEdit} from '../spec/html-blocks.mjs';
2
2
  // SPDX-License-Identifier: MIT
3
- import {finish, finishAsync, cloneTree, cleanTree, serializeTree, htmlBoundary, pause, check} from './render-work.mjs';
3
+ import {finish, finishAsync, cloneTree, cleanTree, serializeTree, htmlParts, admitHtml, htmlBoundary, pause, check} from './render-work.mjs';
4
4
  const RAPIER_TABLE_CAPTION_PREFIX_RE = /^(?:Table:|:) /;
5
5
  const RAPIER_PORTABLE_HIGHLIGHT_HEX = Object.freeze({
6
6
  green: '#35c57a', red: '#f05a62', blue: '#4f91f7', yellow: '#e6b91e', purple: '#9b6dea',
@@ -50,22 +50,32 @@ function initMarkdownIt() {
50
50
  return markOpenDefault(tokens, idx, opts, env, self);
51
51
  };
52
52
 
53
- md.renderer.rules.rapier_color_open = (tokens, idx) => {
54
- const hex = String(tokens[idx].attrGet('data-md-color') || '').toLowerCase();
55
- if (!/^#[0-9a-f]{6}$/.test(hex)) return '<span>';
56
- const dark = _rapierDeriveDarkColor(hex);
57
- return '<span data-md-color="' + hex + '" style="--md-color:' + hex + ';--md-color-dark:' + dark + '">';
58
- };
59
- md.renderer.rules.rapier_color_close = () => '</span>';
60
- md.renderer.rules.rapier_font_open = (tokens, idx) => {
61
- const token = tokens[idx], family = token.attrGet('data-md-font'), css = globalThis.RapierMarkdownSpec.fontFamilyCss(family);
62
- if (!css) return '<span>';
63
- const original = token.attrGet('data-md-font-open');
64
- return '<span data-md-font="' + md.utils.escapeHtml(family) + '"'
65
- + (original ? ' data-md-font-open="' + md.utils.escapeHtml(original) + '"' : '')
66
- + ' style="font-family:' + md.utils.escapeHtml(css) + '">';
53
+ // Every authored spelling lowers to the same admitted style facts. Original delimiters are
54
+ // transient edit-projection data; only the source owner can write them back.
55
+ const renderTypography = (tokens, index) => {
56
+ const typed = globalThis.RapierMarkdownSpec.typography, held = tokens[index].meta?.typography;
57
+ if (!held) return '<span>';
58
+ const facts = held.style, attrs = [], css = [];
59
+ if (facts.size) {
60
+ attrs.push('data-md-size="' + facts.size + '"');
61
+ css.push('font-size:calc(' + typed.size(facts.size).points / 12 + ' * var(--md-unit))');
62
+ }
63
+ if (facts.color) {
64
+ attrs.push('data-md-color="' + facts.color + '"');
65
+ css.push('--md-color:' + facts.color, '--md-color-dark:' + _rapierDeriveDarkColor(facts.color));
66
+ }
67
+ if (facts.family) {
68
+ attrs.push('data-md-font="' + md.utils.escapeHtml(facts.family.name) + '"');
69
+ css.push('font-family:' + facts.family.css);
70
+ }
71
+ attrs.push('data-md-typography-open="' + md.utils.escapeHtml(encodeURIComponent(held.open)) + '"',
72
+ 'data-md-typography-close="' + md.utils.escapeHtml(encodeURIComponent(held.close)) + '"');
73
+ return '<span ' + attrs.join(' ') + ' style="' + md.utils.escapeHtml(css.join(';')) + '">';
67
74
  };
68
- md.renderer.rules.rapier_font_close = () => '</span>';
75
+ for (const kind of ['typography', 'color', 'font']) {
76
+ md.renderer.rules['rapier_' + kind + '_open'] = renderTypography;
77
+ md.renderer.rules['rapier_' + kind + '_close'] = () => '</span>';
78
+ }
69
79
  // Ink: the span carries its opener's body (the grammar's inkOpenBody: a sanitizer drops an attribute holding the
70
80
  // comment's close); the ink layer reads it back through the one grammar and draws the stroke over the words' own boxes.
71
81
  // The span itself shows nothing.
@@ -195,6 +205,11 @@ function initMarkdownIt() {
195
205
  _rapierChromeOwnsId('');
196
206
  const cleanTag = tag => /^<[A-Za-z]/.test(tag) ? tag.replace(rawAttribute, (whole, name, value) => {
197
207
  const key = name.toLowerCase();
208
+ if (key === 'style' && /^<span(?=[\s>])/i.test(tag)) {
209
+ const typed = globalThis.RapierMarkdownSpec.typography;
210
+ const css = typed.style(typed.parseStyle(md.utils.unescapeAll(String(value || '').replace(/^["']|["']$/g, '').replace(/\\/g, '\\\\'))));
211
+ return css ? ' style="' + md.utils.escapeHtml(css) + '"' : '';
212
+ }
198
213
  return key.startsWith('data-') || key === 'for' || key === 'id' && _rapierChromeOwnsId(attributeText(value)) ? '' : whole;
199
214
  }) : tag;
200
215
  md.renderer.rules.html_inline = (tokens, idx) => cleanTag(tokens[idx].content);
@@ -331,9 +346,9 @@ function initMarkdownIt() {
331
346
 
332
347
  {
333
348
  let _mathPromptedOnce = false;
334
- const _renderMath = (src, displayMode, env) => {
349
+ const _renderMath = (src, displayMode, env, authored) => {
335
350
  const marker = displayMode ? '$$' : '$';
336
- const source = marker + src + marker, literal = md.utils.escapeHtml(source);
351
+ const source = authored || marker + src + marker, literal = md.utils.escapeHtml(source);
337
352
  if (env.rapierPage) return '<span data-rapier-math-source="">' + literal + '</span>';
338
353
  const mathProvider = env.rapierProviders === false ? null : _rapierProviders.math;
339
354
  if (!mathProvider || mathProvider.status !== 'ready' || typeof mathProvider.renderToString !== 'function') {
@@ -351,8 +366,8 @@ function initMarkdownIt() {
351
366
  }
352
367
  catch (_) { return '<span data-rapier-math-source="">' + literal + '</span>'; }
353
368
  };
354
- md.renderer.rules.math_inline = (tokens, idx, _opts, env) => _renderMath(tokens[idx].content, false, env);
355
- md.renderer.rules.math_block = (tokens, idx, _opts, env) => wrapDisplayMath(_renderMath(tokens[idx].content, true, env)) + '\n';
369
+ md.renderer.rules.math_inline = (tokens, idx, _opts, env) => _renderMath(tokens[idx].content, false, env, tokens[idx].meta?.mathSource);
370
+ md.renderer.rules.math_block = (tokens, idx, _opts, env) => wrapDisplayMath(_renderMath(tokens[idx].content, true, env, tokens[idx].meta?.mathSource)) + '\n';
356
371
  }
357
372
  globalThis.RapierMarkdownLayout.installMarkdownLayout(md);
358
373
  globalThis.RapierImageAssets.configureParser(md, _rapierSplitOpeningFrontmatter);
@@ -398,7 +413,7 @@ function _rapierRenderSemanticRoot(canonical, metadata, options = {}) {
398
413
  root.querySelectorAll('pre > code').forEach(_rapierNormalizeCodeElement);
399
414
  _rapierMarkTableCaptions(root);
400
415
  _rapierMarkFigureCaptions(root);
401
- _rapierMarkListContinuations(Array.from(root.children));
416
+ _rapierMarkListContinuations(_rapierChildElements(root));
402
417
  _rapierDecorateJumpLists(root);
403
418
  return root;
404
419
  }
@@ -413,20 +428,22 @@ async function _rapierRenderSemanticRootAsync(canonical, metadata, options = {})
413
428
  }
414
429
  const markdown = _rapierSplitOpeningFrontmatter(canonical).body;
415
430
  const fallbackEnv = options.page ? {rapierPage: true} : {};
416
- const parsed = options.parsed || (runtime.parseAsync ? await runtime.parseAsync(markdown, options) : {tokens: md.parse(markdown, fallbackEnv), env: fallbackEnv});
417
- const {tokens, env} = parsed, labels = env.footnotes?.list?.map((item, index) => String(item?.label || ('inline-' + (index + 1))));
431
+ let parsed = options.parsed || (runtime.parseAsync ? await runtime.parseAsync(markdown, options) : {tokens: md.parse(markdown, fallbackEnv), env: fallbackEnv});
432
+ let {tokens, env} = parsed, labels = env.footnotes?.list?.map((item, index) => String(item?.label || ('inline-' + (index + 1))));
433
+ await pause(work, .3);
418
434
  env.rapierDocumentSpace = !!globalThis.RapierMarkdownSpec?.readDocumentSettings?.(canonical)?.space;
419
435
  const root = document.createElement('div'), part = document.createElement('div'), boundary = htmlBoundary();
420
436
  root.setAttribute('data-rapier-semantic-root', 'true');
421
437
  if (env.rapierDocumentSpace) root.setAttribute('data-md-space', '');
422
- let html = '', began = performance.now();
438
+ let html = '', began = performance.now(), complete = false;
423
439
  const append = () => {
424
440
  if (md.renderer.rapierTransformHtml) html = md.renderer.rapierTransformHtml(html);
425
441
  if (labels?.length) html = _relabelFootnoteHtml(html, labels);
426
- part.innerHTML = sanitizeRapierHtml(html, options.page ? 'source' : 'export');
442
+ admitHtml(part, html, sanitizeRapierHtml, runtime.sanitizeRapierFragment, options.page ? 'source' : 'export');
427
443
  while (part.firstChild) root.appendChild(part.firstChild);
428
444
  html = '';
429
445
  };
446
+ try {
430
447
  for (let index = 0; index < tokens.length; index++) {
431
448
  const token = tokens[index], rule = md.renderer.rules[token.type];
432
449
  let rendered = token.type === 'inline' ? md.renderer.renderInline(token.children, md.options, env)
@@ -439,9 +456,15 @@ async function _rapierRenderSemanticRootAsync(canonical, metadata, options = {})
439
456
  }
440
457
  }
441
458
  if (html) append();
459
+ // Token/environment objects are no longer inputs once their complete HTML
460
+ // is admitted. Drop our references before the document-wide DOM passes;
461
+ // never mutate an optional parsed result retained by another caller.
462
+ parsed = tokens = env = labels = null;
442
463
  await finishAsync(_rapierSemanticDecorations(root), work);
443
464
  await pause(work, .6);
465
+ complete = true;
444
466
  return root;
467
+ } finally { if (!complete) { root.replaceChildren(); part.replaceChildren(); } }
445
468
  }
446
469
 
447
470
  function* _rapierSemanticDecorations(root) {
@@ -451,7 +474,7 @@ function* _rapierSemanticDecorations(root) {
451
474
  for (const code of root.querySelectorAll('pre > code')) { _rapierNormalizeCodeElement(code); yield; }
452
475
  yield* _rapierMarkTableCaptionsSteps(root); yield;
453
476
  yield* _rapierMarkFigureCaptionsSteps(root); yield;
454
- yield* _rapierMarkListContinuationsSteps(Array.from(root.children)); yield;
477
+ yield* _rapierMarkListContinuationsSteps(_rapierChildElements(root)); yield;
455
478
  yield* _rapierDecorateJumpListsSteps(root); yield;
456
479
  }
457
480
 
@@ -660,13 +683,20 @@ function _rapierApplyBlockDirection(root) { return finish(_rapierApplyBlockDirec
660
683
 
661
684
  function* _rapierApplyBlockDirectionSteps(root) {
662
685
  if (!root || typeof root.querySelectorAll !== 'function') return;
663
- for (const el of root.querySelectorAll(_RAPIER_DIR_BLOCKS)) {
686
+ const blocks = new Set(_RAPIER_DIR_BLOCKS.split(',')), elements = root.ownerDocument.createTreeWalker(root, 1);
687
+ for (let el = elements.nextNode(); el; el = elements.nextNode()) {
664
688
  yield;
665
- if (el.closest('pre, code')) continue;
689
+ if (!blocks.has(el.localName)) continue;
690
+ let literal = false;
691
+ for (let parent = el.parentElement; parent; parent = parent.parentElement)
692
+ if (parent.localName === 'pre' || parent.localName === 'code') { literal = true; break; }
693
+ if (literal) continue;
666
694
  let dir = 'ltr', found = false;
667
695
  const walker = el.ownerDocument.createTreeWalker(el, 4);
696
+ let scanned = 0;
668
697
  for (let text = walker.nextNode(); text && !found; text = walker.nextNode()) {
669
698
  for (const character of text.nodeValue || '') {
699
+ if (++scanned % 1024 === 0) yield;
670
700
  const strong = _rapierBidiStrong(character.codePointAt(0));
671
701
  if (strong) { dir = strong === 'R' ? 'rtl' : 'ltr'; found = true; break; }
672
702
  }
@@ -826,7 +856,9 @@ function* _rapierDecorateJumpListsSteps(root) {
826
856
  }
827
857
  }
828
858
 
829
- const only = root.children && root.children.length === 1 ? root.firstElementChild : null;
859
+ // Do not retain a live collection on the root that Copy will consume. Some
860
+ // DOM hosts eagerly refresh it on every later removal of a complete unit.
861
+ const first = root.firstElementChild, only = first && !first.nextElementSibling ? first : null;
830
862
  if (only && only.tagName === 'P' && /^jump\s+to:?$/i.test(String(only.textContent || '').trim())) {
831
863
  only.classList.add('rapier-jump-label-candidate');
832
864
  }
@@ -849,20 +881,23 @@ function* _rapierMarkFigureCaptionsSteps(root) {
849
881
 
850
882
  function _rapierMarkListContinuations(blocks) { return finish(_rapierMarkListContinuationsSteps(blocks)); }
851
883
 
884
+ function* _rapierChildElements(root) {
885
+ for (let child = root.firstElementChild; child; child = child.nextElementSibling) yield child;
886
+ }
887
+
852
888
  function* _rapierMarkListContinuationsSteps(blocks) {
853
889
  const isHeading = element => !!element && /^H[1-6]$/.test(element.tagName);
854
- for (const [index, element] of blocks.entries()) { yield;
855
- if (!element || element.tagName !== 'OL') continue;
890
+ let previous = null, headings = [], last = 0;
891
+ for (const element of blocks) { yield;
892
+ if (previous && isHeading(element)) {headings.push(element); continue;}
893
+ if (!element || element.tagName !== 'OL') {previous = null; headings = []; continue;}
856
894
  const start = parseInt(element.getAttribute('start'), 10);
857
- const last = (Number.isFinite(start) ? start : 1) + element.querySelectorAll(':scope > li').length - 1;
858
- const headings = [];
859
- let next = index + 1;
860
- while (isHeading(blocks[next])) headings.push(blocks[next++]);
861
- const following = blocks[next];
862
- if (!headings.length || !following || following.tagName !== 'OL') continue;
863
- if (parseInt(following.getAttribute('start'), 10) !== last + 1) continue;
864
- element.setAttribute('data-rapier-list-continues', '');
865
- for (const heading of headings) { yield;heading.setAttribute('data-rapier-list-through', '');}
895
+ if (previous && headings.length && start === last + 1) {
896
+ previous.setAttribute('data-rapier-list-continues', '');
897
+ for (const heading of headings) { yield;heading.setAttribute('data-rapier-list-through', '');}
898
+ }
899
+ previous = element; headings = [];
900
+ last = (Number.isFinite(start) ? start : 1) + element.querySelectorAll(':scope > li').length - 1;
866
901
  }
867
902
  }
868
903
 
@@ -1031,6 +1066,11 @@ function _rapierProjectPortableRoot(semanticRoot, options) { return finish(_rapi
1031
1066
  async function _rapierProjectPortableRootAsync(semanticRoot, options = {}) { return finishAsync(_rapierProjectPortableRootSteps(semanticRoot, options), options.work); }
1032
1067
 
1033
1068
  function* _rapierProjectPortableRootSteps(semanticRoot, options) {
1069
+ const root = yield* _rapierProjectPortableNodesSteps(semanticRoot, options);
1070
+ return yield* cleanTree(root, sanitizeRapierHtml, 'export', runtime.sanitizeRapierFragment);
1071
+ }
1072
+
1073
+ function* _rapierProjectPortableNodesSteps(semanticRoot, options) {
1034
1074
  const opts = options || {};
1035
1075
  // This projection is HTML-shaped (width baked into a CSS `style`, `data-*` stripped) for the
1036
1076
  // consumers that read HTML -- publishing/fragment export, TXT. DOCX is not an HTML consumer: its
@@ -1040,7 +1080,9 @@ function* _rapierProjectPortableRootSteps(semanticRoot, options) {
1040
1080
  // the semantic root carries them. Ordinary paragraph/list CSS measurements also reach their
1041
1081
  // native reader; no derived private CSS is needed.
1042
1082
  const keepLayoutData = !!opts.keepLayoutData;
1043
- const root = yield* cloneTree(semanticRoot), nativeElements = new Set();
1083
+ // Copy owns its freshly rendered, detached root. Other callers retain their
1084
+ // semantic tree and receive the usual independent portable projection.
1085
+ const root = opts.inPlace ? semanticRoot : yield* cloneTree(semanticRoot), nativeElements = new Set();
1044
1086
  // A source soft break is a reading space, never the editor's invisible token.
1045
1087
  yield* _rapierResolveSoftBreakTokensSteps(root);
1046
1088
  // Word consumes these source-bearing islands as pictures with TeX alternatives. Keep
@@ -1148,15 +1190,16 @@ function* _rapierProjectPortableRootSteps(semanticRoot, options) {
1148
1190
  const imageLayout = element.tagName === 'IMG' ? globalThis.RapierMarkdownLayout.parseLayoutAttribute(element.getAttribute('data-rapier-image-layout')) : null;
1149
1191
  const measurements = keepLayoutData && /^(?:P|H[1-6]|LI|OL|UL)$/.test(element.tagName)
1150
1192
  ? ['text-indent', 'margin-inline-start'].map(property => [property, element.style.getPropertyValue(property)]) : [];
1193
+ // The native table reader admits automatic hyphenation from ordinary paragraph CSS.
1194
+ if (keepLayoutData && element.tagName === 'P' && element.closest('table') && element.style.getPropertyValue('hyphens').trim() === 'auto')
1195
+ measurements.push(['hyphens', 'auto']);
1151
1196
  if (keepLayoutData && element.closest('pre')) for (const property of ['color', 'font-weight', 'font-style'])
1152
1197
  measurements.push([property, element.style.getPropertyValue(property)]);
1153
1198
  if (keepLayoutData && element.tagName === 'BLOCKQUOTE')
1154
1199
  measurements.push(['border-inline-start-color', element.style.borderInlineStartColor]);
1155
1200
  const runTypography = keepLayoutData ? ['font-family', 'font-variant-caps'].map(property => [property, element.style.getPropertyValue(property)]) : [];
1156
1201
 
1157
- const colorHex = element.tagName === 'SPAN' ? element.getAttribute('data-md-color') : null;
1158
- const fontFamily = element.tagName === 'SPAN' ? element.getAttribute('data-md-font') : null;
1159
- const fontCss = fontFamily ? globalThis.RapierMarkdownSpec.fontFamilyCss(fontFamily) : '';
1202
+ const typed = globalThis.RapierMarkdownSpec.typography, typography = typed.nodeStyle(element);
1160
1203
 
1161
1204
  const highlightHex = element.tagName === 'MARK'
1162
1205
  ? (RAPIER_PORTABLE_HIGHLIGHT_HEX[String(element.getAttribute('data-rapier-highlight') || '').toLowerCase()] || RAPIER_PORTABLE_HIGHLIGHT_HEX.yellow)
@@ -1167,6 +1210,10 @@ function* _rapierProjectPortableRootSteps(semanticRoot, options) {
1167
1210
  const isPageBreak = element.hasAttribute('data-md-break');
1168
1211
  for (const attribute of Array.from(element.attributes)) { yield;
1169
1212
  const name = attribute.name.toLowerCase();
1213
+ // Copy reads these inert words from the final admitted nodes, then removes
1214
+ // them before emitting HTML. They grant no rendering or resource authority.
1215
+ if (opts.heldPictures && /^(?:SPAN|IMG)$/.test(element.tagName) &&
1216
+ (name === 'data-rapier-remote-src' || name === 'data-rapier-remote-alt')) continue;
1170
1217
  if (keepLayoutData && (name === 'data-rapier-image-layout' || name === 'data-md-layout' || name === 'data-md-align' || name === 'data-rapier-ink'
1171
1218
  || name === 'data-md-first-line-end' || name === 'data-md-initial-font' || name === 'data-md-initial-size'
1172
1219
  || name === 'data-md-picture-cap-height' || name === 'data-md-picture-cap-top')) continue;
@@ -1192,10 +1239,12 @@ function* _rapierProjectPortableRootSteps(semanticRoot, options) {
1192
1239
  if (imageLayout.width != null) element.removeAttribute('width');
1193
1240
  if (pictureStyle) element.style.cssText = pictureStyle;
1194
1241
  }
1195
- if (colorHex && /^#[0-9a-f]{6}$/i.test(colorHex)) element.style.color = colorHex.toLowerCase();
1196
- if (fontCss) element.style.fontFamily = fontCss;
1197
1242
  if (highlightHex) element.style.backgroundColor = highlightHex;
1198
1243
  if (isTableCaption) { element.style.fontStyle = 'italic'; element.style.color = '#555555'; }
1244
+ // Already validated facts serialize directly. An inert host's partial CSSOM must
1245
+ // not silently discard a quoted family or round an authored decimal.
1246
+ const css = typed.style(typography);
1247
+ if (css) element.setAttribute('style', (element.getAttribute('style') ? element.getAttribute('style') + ';' : '') + css);
1199
1248
 
1200
1249
  if (isPageBreak) {
1201
1250
  const p = document.createElement('p');
@@ -1210,7 +1259,7 @@ function* _rapierProjectPortableRootSteps(semanticRoot, options) {
1210
1259
  literal.style.setProperty('hanging-punctuation', 'none');
1211
1260
  }
1212
1261
 
1213
- return yield* cleanTree(root, sanitizeRapierHtml);
1262
+ return root;
1214
1263
  }
1215
1264
 
1216
1265
  function _rapierPortableHtml(root) {
@@ -1218,6 +1267,69 @@ function _rapierPortableHtml(root) {
1218
1267
  }
1219
1268
  async function _rapierPortableHtmlAsync(root, work) { return finishAsync(serializeTree(root, sanitizeRapierHtml, true), work); }
1220
1269
 
1270
+ // One captured document owns this pipeline and its detached nodes. Global semantic
1271
+ // facts are resolved before projection. Final admission consumes the old tree a
1272
+ // complete fragment at a time; HTML and words then share that admitted tree.
1273
+ async function _rapierClipboardPayloadAsync(canonical, metadata, options = {}) {
1274
+ const work = options.work;
1275
+ let semantic;
1276
+ const part = document.createElement('div');
1277
+ try {
1278
+ semantic = await _rapierRenderSemanticRootAsync(canonical, metadata, {work});
1279
+ if (options.formatted && options.materialize && await options.materialize(semantic) === false) return null;
1280
+ check(work);
1281
+ await finishAsync(_rapierProjectPortableNodesSteps(semantic,
1282
+ {baseName: options.baseName, inPlace: true, heldPictures: true}), work);
1283
+ const words = metadata.docKind === 'markdown' ? _rapierPortableTextWriter({heldPictures: true}) : null;
1284
+ const parts = [];
1285
+ let pictures = false;
1286
+ const plain = await finishAsync((function* () {
1287
+ for (const html of htmlParts(semantic, 16384, true)) {
1288
+ if (html === null) { yield; continue; }
1289
+ // Reparse the complete transformed unit at the existing final export
1290
+ // boundary. Both formats consume these admitted nodes before release.
1291
+ admitHtml(part, html, sanitizeRapierHtml, runtime.sanitizeRapierFragment);
1292
+ yield;
1293
+ if (words) yield* words.write(part);
1294
+ if (options.formatted) {
1295
+ for (const node of part.querySelectorAll('[data-rapier-remote-src],[data-rapier-remote-alt]')) {
1296
+ node.removeAttribute('data-rapier-remote-src'); node.removeAttribute('data-rapier-remote-alt'); yield;
1297
+ }
1298
+ pictures ||= !!part.querySelector('img[src^="data:"]');
1299
+ parts.push(yield* serializeTree(part));
1300
+ }
1301
+ part.replaceChildren(); yield;
1302
+ }
1303
+ return words ? yield* words.finish() : canonical;
1304
+ })(), work);
1305
+ check(work);
1306
+ if (!options.formatted) return {plain};
1307
+ return {plain, html: parts.join('').trim(), withoutPictures: pictures ? async () => {
1308
+ // Only this writer's finished HTML reaches this closure. On refusal,
1309
+ // reconstruct one complete fragment, change pictures to text and admit
1310
+ // that changed output. No full fallback DOM survives the operation.
1311
+ return finishAsync((function* () {
1312
+ const output = [], part = document.createElement('div');
1313
+ try {
1314
+ for (const html of parts) {
1315
+ if (!html.includes('<img')) { output.push(html); yield; continue; }
1316
+ part.innerHTML = html;
1317
+ let changed = false;
1318
+ for (const image of part.querySelectorAll('img[src^="data:"]')) {
1319
+ const replacement = document.createElement('span');
1320
+ replacement.textContent = '[image: ' + (String(image.getAttribute('alt') || 'image').trim() || 'image') + ']';
1321
+ image.replaceWith(replacement); changed = true; yield;
1322
+ }
1323
+ output.push(changed ? yield* serializeTree(part, sanitizeRapierHtml) : html);
1324
+ part.replaceChildren(); yield;
1325
+ }
1326
+ return output.join('').trim();
1327
+ } finally { part.replaceChildren(); }
1328
+ })(), work);
1329
+ } : null};
1330
+ } finally { semantic?.replaceChildren(); part.replaceChildren(); }
1331
+ }
1332
+
1221
1333
  function _rapierNormalizeInlineText(value) {
1222
1334
  return String(value || '')
1223
1335
  .replace(/\u00a0/g, ' ')
@@ -1245,79 +1357,102 @@ function* _rapierHeldPicturesAsWordsSteps(root) {
1245
1357
  return copy;
1246
1358
  }
1247
1359
 
1248
- function _rapierPortableInlineText(node, options) {
1360
+ function _rapierPortableInlineText(node, options) { return finish(_rapierPortableInlineTextSteps(node, options)); }
1361
+
1362
+ function* _rapierPortableInlineTextSteps(node, options) {
1249
1363
  const opts = options || {};
1250
1364
  if (!node) return '';
1365
+ yield;
1251
1366
  if (node.nodeType === Node.TEXT_NODE) return node.nodeValue || '';
1252
1367
  if (node.nodeType !== Node.ELEMENT_NODE) return '';
1253
1368
  const tag = node.tagName;
1369
+ if (opts.heldPictures && /^(?:SPAN|IMG)$/.test(tag) && node.hasAttribute('data-rapier-remote-src'))
1370
+ return _rapierPictureWords(String(node.getAttribute('data-rapier-remote-alt') || '').trim(),
1371
+ String(node.getAttribute('data-rapier-remote-src') || '').trim());
1254
1372
  if (tag === 'BR') return '\n';
1255
1373
  // A task's box is what the person sees of it.
1256
1374
  if (tag === 'INPUT') return /^checkbox$/i.test(node.type || '') ? (node.checked || node.hasAttribute('checked') ? '☑ ' : '☐ ') : '';
1257
1375
  if (tag === 'IMG') {
1258
1376
  if (opts.visible) return '';
1259
- const alt = String(node.getAttribute('alt') || '').trim();
1260
- const src = String(node.getAttribute('src') || '').trim();
1261
- return _rapierPictureWords(alt, src);
1377
+ return _rapierPictureWords(String(node.getAttribute('alt') || '').trim(), String(node.getAttribute('src') || '').trim());
1378
+ }
1379
+ if (tag === 'CODE' && !opts.inPre) {
1380
+ const source = String(node.textContent || '');
1381
+ return opts.visible ? source : source ? '`' + source + '`' : '';
1262
1382
  }
1383
+ const parts = [];
1384
+ for (let child = node.firstChild; child; child = child.nextSibling) parts.push(yield* _rapierPortableInlineTextSteps(child, opts));
1385
+ const text = parts.join('');
1263
1386
  if (tag === 'A') {
1264
- const label = _rapierNormalizeInlineText(Array.from(node.childNodes)
1265
- .map(child => _rapierPortableInlineText(child, opts)).join(''));
1266
- const href = String(node.getAttribute('href') || '').trim();
1387
+ const label = _rapierNormalizeInlineText(text), href = String(node.getAttribute('href') || '').trim();
1267
1388
  if (opts.visible || !href || href.charAt(0) === '#') return label;
1268
1389
  if (!label || label === href) return href;
1269
1390
  return label + ' (' + href + ')';
1270
1391
  }
1271
- if (tag === 'CODE' && !opts.inPre) {
1272
- const source = String(node.textContent || '');
1273
- return opts.visible ? source : source ? '`' + source + '`' : '';
1274
- }
1275
- return Array.from(node.childNodes)
1276
- .map(child => _rapierPortableInlineText(child, opts)).join('');
1392
+ return text;
1277
1393
  }
1278
1394
 
1279
- function _rapierPortableListText(list, depth, options) {
1280
- const opts = options || {};
1281
- const level = Math.max(0, depth | 0);
1282
- const ordered = list.tagName === 'OL';
1395
+ function _rapierPortableListText(list, depth, options) { return finish(_rapierPortableListTextSteps(list, depth, options)); }
1396
+
1397
+ function* _rapierPortableListTextSteps(list, depth, options) {
1398
+ const opts = options || {}, level = Math.max(0, depth | 0), ordered = list.tagName === 'OL';
1283
1399
  let counter = ordered ? (parseInt(list.getAttribute('start'), 10) || 1) : 0;
1284
1400
  const lines = [];
1285
- Array.from(list.children).filter(child => child.tagName === 'LI').forEach(item => {
1286
- const nested = Array.from(item.children).filter(child => child.tagName === 'UL' || child.tagName === 'OL');
1287
- const content = Array.from(item.childNodes)
1288
- .filter(child => !(child.nodeType === Node.ELEMENT_NODE && (child.tagName === 'UL' || child.tagName === 'OL')))
1289
- .map(child => _rapierPortableInlineText(child, opts)).join('');
1290
- const normalized = _rapierNormalizeInlineText(content);
1291
- const indent = ' '.repeat(level);
1401
+ for (let item = list.firstElementChild; item; item = item.nextElementSibling) {
1402
+ yield;
1403
+ if (item.tagName !== 'LI') continue;
1404
+ const nested = [], content = [];
1405
+ for (let child = item.firstChild; child; child = child.nextSibling) {
1406
+ if (child.nodeType === Node.ELEMENT_NODE && (child.tagName === 'UL' || child.tagName === 'OL')) nested.push(child);
1407
+ else content.push(yield* _rapierPortableInlineTextSteps(child, opts));
1408
+ }
1409
+ const normalized = _rapierNormalizeInlineText(content.join('')), indent = ' '.repeat(level);
1292
1410
  // A task shows its box where a bullet would be.
1293
1411
  const marker = /^[☐☑☒]/.test(normalized) ? '' : ordered ? (counter++) + '. ' : '• ';
1294
- const parts = (normalized || '').split('\n');
1412
+ const parts = normalized.split('\n');
1295
1413
  lines.push(indent + marker + (parts.shift() || ''));
1296
- parts.forEach(part => lines.push(indent + ' ' + part));
1297
- nested.forEach(child => {
1298
- const nestedText = _rapierPortableListText(child, level + 1, opts);
1299
- if (nestedText) lines.push(nestedText);
1300
- });
1301
- });
1414
+ for (const part of parts) { lines.push(indent + ' ' + part); yield; }
1415
+ for (const child of nested) {
1416
+ const text = yield* _rapierPortableListTextSteps(child, level + 1, opts);
1417
+ if (text) lines.push(text);
1418
+ }
1419
+ }
1302
1420
  return lines.join('\n');
1303
1421
  }
1304
1422
 
1305
1423
  // A table, or a part of one a selection clipped out (a head, a body, a row): its rows.
1306
- function _rapierPortableTableText(table, options) {
1307
- const opts = options || {};
1308
- const rows = table.tagName === 'TR' ? [table] : Array.from(table.querySelectorAll('tr'));
1309
- return rows.map(row => {
1310
- return Array.from(row.children)
1311
- .filter(cell => cell.tagName === 'TH' || cell.tagName === 'TD')
1312
- .map(cell => _rapierNormalizeInlineText(_rapierPortableInlineText(cell, opts)))
1313
- .join(opts.visible ? '\t' : ' | ');
1314
- }).filter(Boolean).join('\n');
1424
+ function _rapierPortableTableText(table, options) { return finish(_rapierPortableTableTextSteps(table, options)); }
1425
+
1426
+ function* _rapierPortableTableTextSteps(table, options) {
1427
+ const opts = options || {}, rows = [];
1428
+ const walker = document.createTreeWalker(table, 1 /* SHOW_ELEMENT */);
1429
+ let row = table.tagName === 'TR' ? table : walker.nextNode();
1430
+ while (row) {
1431
+ yield;
1432
+ if (row.tagName === 'TR') {
1433
+ const cells = [];
1434
+ for (let cell = row.firstElementChild; cell; cell = cell.nextElementSibling) {
1435
+ if (cell.tagName === 'TH' || cell.tagName === 'TD')
1436
+ cells.push(_rapierNormalizeInlineText(yield* _rapierPortableInlineTextSteps(cell, opts)));
1437
+ }
1438
+ const text = cells.join(opts.visible ? '\t' : ' | ');
1439
+ if (text) rows.push(text);
1440
+ }
1441
+ row = table.tagName === 'TR' ? null : walker.nextNode();
1442
+ }
1443
+ return rows.join('\n');
1315
1444
  }
1316
1445
 
1317
1446
  function _rapierPortablePlainText(root, options) { return finish(_rapierPortablePlainTextSteps(root, options)); }
1318
1447
  async function _rapierPortablePlainTextAsync(root, options = {}) { return finishAsync(_rapierPortablePlainTextSteps(root, options), options.work); }
1319
1448
 
1320
1449
  function* _rapierPortablePlainTextSteps(root, options) {
1450
+ const writer = _rapierPortableTextWriter(options);
1451
+ yield* writer.write(root);
1452
+ return yield* writer.finish();
1453
+ }
1454
+
1455
+ function _rapierPortableTextWriter(options) {
1321
1456
  const opts = options || {};
1322
1457
  const blocks = [];
1323
1458
  const blockTags = new Set([
@@ -1332,49 +1467,48 @@ function* _rapierPortablePlainTextSteps(root, options) {
1332
1467
  if (value) blocks.push({ text: value, preserve: !!preserve });
1333
1468
  };
1334
1469
 
1335
- const collectChildren = function* (parent) {
1336
- let inline = [];
1337
- const flush = () => {
1338
- if (!inline.length) return;
1339
- pushBlock(_rapierNormalizeInlineText(inline.map(node => _rapierPortableInlineText(node, opts)).join('')), false);
1340
- inline = [];
1341
- };
1470
+ const state = () => ({inline: [], table: null});
1471
+ const flush = current => {
1472
+ if (current.inline.length) {
1473
+ pushBlock(_rapierNormalizeInlineText(current.inline.join('')), false);
1474
+ current.inline = [];
1475
+ }
1476
+ if (current.table) {
1477
+ pushBlock(current.table.filter(Boolean).join('\n'), false);
1478
+ current.table = null;
1479
+ }
1480
+ };
1481
+ const collectChildren = function* (parent, current = state(), complete = true) {
1342
1482
  const tablePart = node => node.nodeType === Node.ELEMENT_NODE && /^(THEAD|TBODY|TFOOT|TR)$/.test(node.tagName);
1343
1483
  const blankText = node => node.nodeType === Node.TEXT_NODE && !String(node.nodeValue || '').trim();
1344
- const children = Array.from(parent.childNodes);
1345
- const inTableRun = index => { for (let at = index - 1; at >= 0; at--) { if (!blankText(children[at])) return tablePart(children[at]); } return false; };
1346
- for (const [index, child] of children.entries()) {
1484
+ for (let child = parent.firstChild; child; child = child.nextSibling) {
1347
1485
  yield;
1348
1486
  if (tablePart(child)) {
1349
- // The parts of a table a selection clipped out of it: one block of rows, whichever parts arrived.
1350
- if (inTableRun(index)) continue;
1351
- flush();
1352
- const parts = [];
1353
- for (let at = index; at < children.length && (tablePart(children[at]) || blankText(children[at])); at++) {
1354
- if (tablePart(children[at])) parts.push(_rapierPortableTableText(children[at], opts));
1355
- }
1356
- pushBlock(parts.filter(Boolean).join('\n'), false);
1357
- } else if (blankText(child) && inTableRun(index)) {
1487
+ // A clipped table can cross an output fragment. Retain its words,
1488
+ // not detached rows, until the next non-table node closes the block.
1489
+ if (!current.table) { flush(current); current.table = []; }
1490
+ current.table.push(yield* _rapierPortableTableTextSteps(child, opts));
1358
1491
  continue;
1359
- } else if (child.nodeType === Node.ELEMENT_NODE && blockTags.has(child.tagName)) {
1360
- flush();
1492
+ }
1493
+ if (current.table && blankText(child)) continue;
1494
+ if (current.table) flush(current);
1495
+ if (child.nodeType === Node.ELEMENT_NODE && blockTags.has(child.tagName)) {
1496
+ flush(current);
1361
1497
  yield* collectBlock(child);
1362
1498
  } else if (opts.visible && child.nodeType === Node.ELEMENT_NODE && child.tagName === 'CODE' && String(child.textContent || '').includes('\n')) {
1363
- // Lines of a code block taken without their block: the code as shown, every space kept.
1364
- flush();
1499
+ // Lines clipped from a code block retain every authored space.
1500
+ flush(current);
1365
1501
  pushBlock(String(child.textContent || ''), true);
1366
- } else {
1367
- inline.push(child);
1368
- }
1502
+ } else current.inline.push(yield* _rapierPortableInlineTextSteps(child, opts));
1369
1503
  }
1370
- flush();
1504
+ if (complete) flush(current);
1371
1505
  };
1372
1506
 
1373
1507
  const collectBlock = function* (node) {
1374
1508
  if (!node || node.nodeType !== Node.ELEMENT_NODE) return;
1375
1509
  const tag = node.tagName;
1376
1510
  if (/^H[1-6]$/.test(tag) || tag === 'P') {
1377
- const text = _rapierNormalizeInlineText(_rapierPortableInlineText(node, opts));
1511
+ const text = _rapierNormalizeInlineText(yield* _rapierPortableInlineTextSteps(node, opts));
1378
1512
  // An empty paragraph (the line Enter makes) is a line of its own, as it is in Word's plain text: one newline more between its neighbours.
1379
1513
  if (tag === 'P' && !text && node.childNodes.length && !node.hasAttribute('data-md-break')) blocks.push({ text: '', blank: true });
1380
1514
  else pushBlock(text, false);
@@ -1391,11 +1525,11 @@ function* _rapierPortablePlainTextSteps(root, options) {
1391
1525
  return;
1392
1526
  }
1393
1527
  if (tag === 'UL' || tag === 'OL') {
1394
- pushBlock(_rapierPortableListText(node, 0, opts), false);
1528
+ pushBlock(yield* _rapierPortableListTextSteps(node, 0, opts), false);
1395
1529
  return;
1396
1530
  }
1397
1531
  if (tag === 'TABLE') {
1398
- pushBlock(_rapierPortableTableText(node, opts), false);
1532
+ pushBlock(yield* _rapierPortableTableTextSteps(node, opts), false);
1399
1533
  return;
1400
1534
  }
1401
1535
  if (tag === 'BLOCKQUOTE') {
@@ -1409,10 +1543,10 @@ function* _rapierPortablePlainTextSteps(root, options) {
1409
1543
  const children = Array.from(node.children);
1410
1544
  for (let i = 0; i < children.length; i++) {
1411
1545
  if (children[i].tagName !== 'DT') continue;
1412
- const term = _rapierNormalizeInlineText(_rapierPortableInlineText(children[i], opts));
1546
+ const term = _rapierNormalizeInlineText(yield* _rapierPortableInlineTextSteps(children[i], opts));
1413
1547
  const defs = [];
1414
1548
  while (children[i + 1] && children[i + 1].tagName === 'DD') {
1415
- defs.push(_rapierNormalizeInlineText(_rapierPortableInlineText(children[++i], opts)));
1549
+ defs.push(_rapierNormalizeInlineText(yield* _rapierPortableInlineTextSteps(children[++i], opts)));
1416
1550
  }
1417
1551
  pushBlock(term + (defs.length ? ' — ' + defs.join('\n ') : ''), false);
1418
1552
  }
@@ -1420,14 +1554,16 @@ function* _rapierPortablePlainTextSteps(root, options) {
1420
1554
  }
1421
1555
  if (tag === 'DETAILS') {
1422
1556
  const summary = node.querySelector(':scope > summary');
1423
- if (summary) pushBlock(_rapierNormalizeInlineText(_rapierPortableInlineText(summary, opts)), false);
1557
+ if (summary) pushBlock(_rapierNormalizeInlineText(yield* _rapierPortableInlineTextSteps(summary, opts)), false);
1424
1558
  for (const child of node.children) if (child !== summary) { yield; yield* collectBlock(child); }
1425
1559
  return;
1426
1560
  }
1427
1561
  yield* collectChildren(node);
1428
1562
  };
1429
1563
 
1430
- yield* collectChildren(root);
1564
+ const pending = state();
1565
+ return {write: root => collectChildren(root, pending, false), finish: function* () {
1566
+ flush(pending);
1431
1567
  // Each block stands a blank line from the next, and each empty paragraph between them adds one newline more.
1432
1568
  let joined = '', blanks = 0;
1433
1569
  for (const block of blocks) {
@@ -1438,6 +1574,7 @@ function* _rapierPortablePlainTextSteps(root, options) {
1438
1574
  }
1439
1575
  const text = joined.trim();
1440
1576
  return opts.finalNewline && text ? text + '\n' : text;
1577
+ }};
1441
1578
  }
1442
1579
 
1443
1580
  // The text file Export writes: a Markdown document's portable words and one final newline, any other document's own text.
@@ -1490,7 +1627,7 @@ function _rapierBidiStrong(cp) {
1490
1627
  return null;
1491
1628
  }
1492
1629
 
1493
- return {_rapierPlainTextFileAsync, _rapierCloneRootAsync, _rapierPortablePlainTextAsync, _rapierHeldPicturesAsWordsAsync, _rapierRenderSemanticRootAsync, _rapierProjectPortableRootAsync, _rapierPortableHtmlAsync, _rapierCalloutInfo, _rapierRemoveCalloutMarker, _rapierMathSource, _rapierFlattenPortableDetails, _rapierProjectPortableRoot, _rapierPortableHtml, _rapierNormalizeInlineText, _rapierPortableInlineText, _rapierPortableListText, _rapierPortableTableText, _rapierPortablePlainText, _rapierPlainTextFile, _rapierHeldPicturesAsWords, _rapierSourceTokenValue, _rapierResolveSoftBreakTokens, _rapierUnwrapElement, _rapierBidiStrong, render: (source, metadata = {}) => { if (!md) initMarkdownIt(); return _rapierRenderSemanticRoot(source, metadata).innerHTML; }, initMarkdownIt, _rapierRenderSemanticRoot, _rapierInstallSemanticProbe, _rapierImageAltSize, _rapierImageAltSourceParts, _rapierImageAltText, _rapierImageSizeAttributes, _rapierRawHtmlFragment, _rapierApplyBlockDirection, _rapierFirstStrongDir, _rapierAssignHeadingSlugs, _rapierDisambiguateRenderedAnchors, _rapierDecodeHash, _rapierDecorateJumpLists, _rapierMarkFigureCaptions, _rapierMarkListContinuations, _rapierMarkTableCaptions, _rapierNormalizeCodeElement, _rapierLanguageClass, _relabelFootnoteHtml, _footnoteIdFromLabel, wrapDisplayMath, parser: () => md};
1630
+ return {_rapierClipboardPayloadAsync, _rapierPlainTextFileAsync, _rapierCloneRootAsync, _rapierPortablePlainTextAsync, _rapierHeldPicturesAsWordsAsync, _rapierRenderSemanticRootAsync, _rapierProjectPortableRootAsync, _rapierPortableHtmlAsync, _rapierCalloutInfo, _rapierRemoveCalloutMarker, _rapierMathSource, _rapierFlattenPortableDetails, _rapierProjectPortableRoot, _rapierPortableHtml, _rapierNormalizeInlineText, _rapierPortableInlineText, _rapierPortableListText, _rapierPortableTableText, _rapierPortablePlainText, _rapierPlainTextFile, _rapierHeldPicturesAsWords, _rapierSourceTokenValue, _rapierResolveSoftBreakTokens, _rapierUnwrapElement, _rapierBidiStrong, render: (source, metadata = {}) => { if (!md) initMarkdownIt(); return _rapierRenderSemanticRoot(source, metadata).innerHTML; }, initMarkdownIt, _rapierRenderSemanticRoot, _rapierInstallSemanticProbe, _rapierImageAltSize, _rapierImageAltSourceParts, _rapierImageAltText, _rapierImageSizeAttributes, _rapierRawHtmlFragment, _rapierApplyBlockDirection, _rapierFirstStrongDir, _rapierAssignHeadingSlugs, _rapierDisambiguateRenderedAnchors, _rapierDecodeHash, _rapierDecorateJumpLists, _rapierMarkFigureCaptions, _rapierMarkListContinuations, _rapierMarkTableCaptions, _rapierNormalizeCodeElement, _rapierLanguageClass, _relabelFootnoteHtml, _footnoteIdFromLabel, wrapDisplayMath, parser: () => md};
1494
1631
  }
1495
1632
  export {RAPIER_TABLE_CAPTION_PREFIX_RE as tableCaptionPrefix, RAPIER_PORTABLE_HIGHLIGHT_HEX as portableHighlightHex, _RAPIER_RTL_SCRIPT as rtlScripts, createMarkdownRenderer};
1496
1633