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.
- package/LICENSE +2 -2
- package/README.md +213 -53
- package/dist/agent/vendor/hyphen-en-us/LICENSE +14 -0
- package/dist/agent/vendor/hyphen-en-us/SOURCE.json +33 -0
- package/dist/agent/vendor/hyphen-en-us/hyph-en-us.hyp.txt +14 -0
- package/dist/agent/vendor/hyphen-en-us/hyph-en-us.pat.txt +4938 -0
- package/dist/agent/vendor/hyphen-en-us/patterns.js +25 -0
- package/dist/agent/vendor/pretext/LICENSE +208 -0
- package/dist/agent/vendor/pretext/NOTICE +26 -13
- package/dist/agent/vendor/pretext/SOURCE.json +213 -45
- package/dist/agent/vendor/pretext/analysis.js +675 -1224
- package/dist/agent/vendor/pretext/entry-geometry.js +183 -0
- package/dist/agent/vendor/pretext/gecko-line-breaks.js +688 -0
- package/dist/agent/vendor/pretext/generated/engine-break-data.js +57 -0
- package/dist/agent/vendor/pretext/generated/webkit-generic-families.js +80 -0
- package/dist/agent/vendor/pretext/graphemes.js +98 -0
- package/dist/agent/vendor/pretext/han-kerning.js +253 -0
- package/dist/agent/vendor/pretext/layout.js +352 -290
- package/dist/agent/vendor/pretext/line-break.js +1154 -588
- package/dist/agent/vendor/pretext/line-breaks.js +892 -0
- package/dist/agent/vendor/pretext/line-text.js +81 -39
- package/dist/agent/vendor/pretext/measurement.js +1105 -176
- package/dist/agent/vendor/pretext/prepare.js +807 -0
- package/dist/agent/vendor/pretext/rich-inline.js +1242 -273
- package/dist/agent/will.mjs +2 -0
- package/dist/kit/assets.mjs +5 -1
- package/dist/kit/conformance/run.mjs +4 -4
- package/dist/kit/export-fonts.mjs +26 -11
- package/dist/kit/index.mjs +1 -0
- package/dist/kit/ledger/carried.mjs +9 -7
- package/dist/kit/ledger/data.mjs +73 -13
- package/dist/kit/ledger/format.mjs +166 -53
- package/dist/kit/ledger/hash.mjs +75 -18
- package/dist/kit/ledger/history.mjs +143 -115
- package/dist/kit/ledger/inverse-source.mjs +158 -19
- package/dist/kit/ledger/journal-records.mjs +292 -67
- package/dist/kit/model.mjs +1 -1
- package/dist/kit/pdf-glyphs.mjs +4 -4
- package/dist/kit/render-markdown.mjs +259 -122
- package/dist/kit/render-sanitize.mjs +24 -4
- package/dist/kit/render-work.mjs +99 -20
- package/dist/kit/render.mjs +101 -65
- package/dist/kit/typography.mjs +2 -0
- package/dist/layout/hyphenation.mjs +127 -0
- package/dist/layout/line-plan.mjs +88 -96
- package/dist/layout/model.mjs +61 -80
- package/dist/layout/paragraph-fit.mjs +191 -0
- package/dist/layout/preparation.mjs +373 -0
- package/dist/layout/prepared-paragraph.mjs +223 -0
- package/dist/layout/typography.mjs +345 -111
- package/dist/packages/rapier-diff/index.mjs +288 -196
- package/dist/spec/document-fonts.mjs +13 -4
- package/dist/spec/md-assets.mjs +149 -30
- package/dist/spec/md-layout.mjs +5 -2
- package/dist/spec/md-marks.mjs +218 -3
- package/dist/spec/md-source.mjs +122 -0
- package/package.json +2 -1
- package/style.css +9 -0
- package/dist/agent/vendor/pretext/bidi.js +0 -151
- 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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
return '<span
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
417
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
855
|
-
|
|
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
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
1281
|
-
|
|
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
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
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 =
|
|
1412
|
+
const parts = normalized.split('\n');
|
|
1295
1413
|
lines.push(indent + marker + (parts.shift() || ''));
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
const
|
|
1299
|
-
if (
|
|
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
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
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
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
1350
|
-
|
|
1351
|
-
flush();
|
|
1352
|
-
|
|
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
|
-
}
|
|
1360
|
-
|
|
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
|
|
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(
|
|
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(
|
|
1528
|
+
pushBlock(yield* _rapierPortableListTextSteps(node, 0, opts), false);
|
|
1395
1529
|
return;
|
|
1396
1530
|
}
|
|
1397
1531
|
if (tag === 'TABLE') {
|
|
1398
|
-
pushBlock(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
|