rapier-markdown-kit 1.1.102 → 1.2.3
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 +5 -2
- package/README.md +245 -54
- 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/images/gif.mjs +47 -0
- package/dist/images/playback.mjs +101 -0
- package/dist/kit/assets.mjs +5 -1
- package/dist/kit/conformance/03-wrap-around-silhouette.md +1 -1
- package/dist/kit/conformance/04-wrap-box.md +1 -1
- package/dist/kit/conformance/05-behind.md +1 -1
- package/dist/kit/conformance/06-front.md +1 -1
- package/dist/kit/conformance/07-rotate-raster.md +1 -1
- package/dist/kit/conformance/08-drawing-rotation.md +1 -1
- package/dist/kit/conformance/09-drawing-ring-interior.expected.json +18 -18
- package/dist/kit/conformance/09-drawing-ring-interior.md +1 -1
- package/dist/kit/conformance/10-both-sides.md +1 -1
- package/dist/kit/conformance/11-neighbour-skips-image.md +1 -1
- package/dist/kit/conformance/{12-heading-barrier.expected.json → 12-rule-barrier.expected.json} +1 -1
- package/dist/kit/conformance/{12-heading-barrier.md → 12-rule-barrier.md} +2 -2
- package/dist/kit/conformance/README.md +19 -28
- package/dist/kit/conformance/run.mjs +13 -11
- package/dist/kit/export-fonts.mjs +26 -11
- package/dist/kit/index.mjs +1 -0
- package/dist/kit/ledger/carried.mjs +17 -10
- package/dist/kit/ledger/data.mjs +73 -13
- package/dist/kit/ledger/format.mjs +176 -54
- package/dist/kit/ledger/hash.mjs +91 -18
- package/dist/kit/ledger/history.mjs +156 -116
- package/dist/kit/ledger/inverse-source.mjs +158 -19
- package/dist/kit/ledger/journal-records.mjs +292 -67
- package/dist/kit/ledger/merge.mjs +4 -3
- package/dist/kit/model.mjs +1 -1
- package/dist/kit/pdf-glyphs.mjs +4 -4
- package/dist/kit/pdf-vectors.mjs +4 -1
- package/dist/kit/rapier-ledger.mjs +2 -2
- package/dist/kit/render-ink.mjs +1 -1
- package/dist/kit/render-markdown.mjs +400 -148
- package/dist/kit/render-print.mjs +131 -55
- package/dist/kit/render-sanitize.mjs +30 -7
- package/dist/kit/render-work.mjs +99 -20
- package/dist/kit/render.mjs +133 -140
- 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 +71 -88
- 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 +359 -224
- package/dist/spec/document-fonts.mjs +14 -5
- package/dist/spec/document-settings.mjs +11 -164
- package/dist/spec/html-blocks.mjs +618 -79
- package/dist/spec/md-assets.mjs +178 -36
- package/dist/spec/md-layout.mjs +13 -2
- package/dist/spec/md-marks.mjs +220 -127
- package/dist/spec/md-source.mjs +157 -0
- package/package.json +2 -1
- package/style.css +15 -50
- 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
|
-
import {htmlPlaceholder, mountHtmlBlocks, htmlImageRecord, htmlLayoutEdit} from '../spec/html-blocks.mjs';
|
|
2
1
|
// SPDX-License-Identifier: MIT
|
|
3
|
-
import {
|
|
2
|
+
import {htmlPlaceholder, mountHtmlBlocks, materializeObjects, objectPosterLabel, htmlImageRecord, htmlLayoutEdit, htmlModeEdit, mergeHtmlObjectBlocks} from '../spec/html-blocks.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,30 @@ 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
|
-
md.renderer.rules.
|
|
75
|
+
md.renderer.rules.rapier_typography_open = renderTypography;
|
|
76
|
+
md.renderer.rules.rapier_typography_close = () => '</span>';
|
|
69
77
|
// Ink: the span carries its opener's body (the grammar's inkOpenBody: a sanitizer drops an attribute holding the
|
|
70
78
|
// comment's close); the ink layer reads it back through the one grammar and draws the stroke over the words' own boxes.
|
|
71
79
|
// The span itself shows nothing.
|
|
@@ -128,8 +136,8 @@ function initMarkdownIt() {
|
|
|
128
136
|
const asset = globalThis.RapierImageAssets.dataImage(src), reference = token.meta?.mdImage?.reference;
|
|
129
137
|
// A connected host's own asset; a host without one (the server) supplies no port.
|
|
130
138
|
if (!env.rapierPage && typeof globalThis.RapierEmbeddedImages?.imageHtml === 'function' &&
|
|
131
|
-
(_rapierEmbedAssetSource?.(src) || asset && (reference || asset.codec === 'image/jxl' || asset.codec === 'image/svg+xml'))) return globalThis.RapierEmbeddedImages.imageHtml(
|
|
132
|
-
reference, alt, title, size, layout, rawAlt, token.meta?.mdImage?.source, src);
|
|
139
|
+
(_rapierEmbedAssetSource?.(src) || asset && (reference || asset.codec === 'image/jxl' || asset.codec === 'image/gif' || asset.codec === 'image/svg+xml'))) return globalThis.RapierEmbeddedImages.imageHtml(
|
|
140
|
+
reference, alt, title, size, layout, rawAlt, token.meta?.mdImage?.source, src, env.rapierImagePresentation !== false);
|
|
133
141
|
const drawingAlt = typeof rawAlt === 'string' && alt === '' && asset?.codec === 'image/svg+xml' ? globalThis.RapierImageAssets.drawingImageAlt(src) : null;
|
|
134
142
|
const shownAlt = drawingAlt ?? alt;
|
|
135
143
|
let loadable = false;
|
|
@@ -148,19 +156,28 @@ function initMarkdownIt() {
|
|
|
148
156
|
return html.replace('<span ', '<span data-rapier-markdown-image="" ' +
|
|
149
157
|
(token.meta?.mdImage?.source ? 'data-rapier-image-source="' + md.utils.escapeHtml(encodeURIComponent(token.meta.mdImage.source)) + '" ' : ''));
|
|
150
158
|
}
|
|
159
|
+
let animation = null;
|
|
160
|
+
try {animation = globalThis.RapierImageAssets.imageAnimation?.(src);} catch (_) {}
|
|
151
161
|
const attrs = [
|
|
152
162
|
'data-rapier-markdown-image=""',
|
|
153
|
-
'src="' + md.utils.escapeHtml(src) + '"',
|
|
163
|
+
'src="' + md.utils.escapeHtml(animation?.first || src) + '"',
|
|
154
164
|
'alt="' + md.utils.escapeHtml(shownAlt) + '"',
|
|
155
165
|
'loading="lazy"',
|
|
156
166
|
'decoding="async"',
|
|
157
167
|
];
|
|
168
|
+
if (animation) attrs.push('data-rapier-animation="' + md.utils.escapeHtml(src) + '"',
|
|
169
|
+
'data-rapier-natural-width="' + animation.width + '"', 'data-rapier-natural-height="' + animation.height + '"');
|
|
158
170
|
if (token.meta?.mdImage?.source && token.meta.mdImage.source.length <= 4096)
|
|
159
171
|
attrs.push('data-rapier-image-source="' + md.utils.escapeHtml(encodeURIComponent(token.meta.mdImage.source)) + '"');
|
|
160
172
|
if (title) attrs.push('title="' + md.utils.escapeHtml(title) + '"');
|
|
161
173
|
if (shownAlt !== alt || size && !token.meta?.mdLayout?.layout?.width) attrs.push('data-rapier-image-alt-source="' + md.utils.escapeHtml(rawAlt) + '"');
|
|
162
174
|
if (layout) attrs.push('data-rapier-image-layout="' + md.utils.escapeHtml(encodeURIComponent(layout)) + '"');
|
|
163
|
-
|
|
175
|
+
// Portable semantic consumers keep their existing native image attributes. Only
|
|
176
|
+
// live editor/reader presentation needs provisional geometry and its load consumer.
|
|
177
|
+
if (env.rapierImagePresentation === false) return '<img ' + attrs.join(' ') + _rapierImageSizeAttributes(size, layout) + '>';
|
|
178
|
+
const geometry = !env.rapierPage && globalThis.RapierEmbeddedImages?.geometry
|
|
179
|
+
? globalThis.RapierEmbeddedImages.geometry(src) : globalThis.RapierImageAssets.imagePlaceholderDimensions(src);
|
|
180
|
+
return '<img ' + attrs.join(' ') + _rapierImageSizeAttributes(size, layout, geometry || {}) + '>';
|
|
164
181
|
};
|
|
165
182
|
|
|
166
183
|
md.renderer.rules.hr = function (tokens, idx) {
|
|
@@ -176,7 +193,9 @@ function initMarkdownIt() {
|
|
|
176
193
|
// markup reaches DOMPurify: sanitizing each HTML token closes containers before their body.
|
|
177
194
|
// The harness runs this installer alone for the parser's acceptance; without the page's sanitizer the tokens pass.
|
|
178
195
|
const inPage = typeof sanitizeRapierHtml === 'function' && typeof _rapierChromeOwnsId === 'function';
|
|
179
|
-
|
|
196
|
+
// HTML separates attributes and ends an unquoted value only at ASCII whitespace: JavaScript's \s also takes U+00A0,
|
|
197
|
+
// which would read a quoted value the browser never sees and keep the attributes hidden inside it.
|
|
198
|
+
const rawAttribute = /[\t\n\f\r ]+([A-Za-z_:][A-Za-z0-9_.:-]*)(?:[\t\n\f\r ]*=[\t\n\f\r ]*("[^"]*"|'[^']*'|[^"'=<>`\t\n\f\r ]+))?/g;
|
|
180
199
|
// The id is read as HTML reads it: a numeric character reference (`source-textarea` is `source-textarea`,
|
|
181
200
|
// `s` the same) and the named references that spell a character an id can hold are decoded before the
|
|
182
201
|
// chrome's ids are asked; every other name stays literal (no named reference spells a letter, a digit or a
|
|
@@ -186,7 +205,7 @@ function initMarkdownIt() {
|
|
|
186
205
|
equals: '=', comma: ',', lpar: '(', rpar: ')', lsqb: '[', lbrack: '[', rsqb: ']', rbrack: ']', lcub: '{', lbrace: '{',
|
|
187
206
|
rcub: '}', rbrace: '}', verbar: '|', vert: '|', VerticalLine: '|', bsol: '\\', Hat: '^', grave: '`', DiacriticalGrave: '`', Tab: '\t', NewLine: '\n' };
|
|
188
207
|
const attributeText = value => String(value || '').replace(/^["']|["']$/g, '')
|
|
189
|
-
.replace(/&(?:#(\d
|
|
208
|
+
.replace(/&(?:#(\d+)|#[xX]([0-9a-fA-F]+)|([A-Za-z][A-Za-z0-9]{0,31}));?/g, (whole, dec, hex, name) => {
|
|
190
209
|
const code = dec ? Number(dec) : hex ? parseInt(hex, 16) : -1;
|
|
191
210
|
if (code >= 0) return code > 0 && code <= 0x10ffff ? String.fromCodePoint(code) : '\ufffd';
|
|
192
211
|
return Object.hasOwn(RAW_ID_NAMED, name) ? RAW_ID_NAMED[name] : whole;
|
|
@@ -195,12 +214,23 @@ function initMarkdownIt() {
|
|
|
195
214
|
_rapierChromeOwnsId('');
|
|
196
215
|
const cleanTag = tag => /^<[A-Za-z]/.test(tag) ? tag.replace(rawAttribute, (whole, name, value) => {
|
|
197
216
|
const key = name.toLowerCase();
|
|
217
|
+
if (key === 'style' && /^<span(?=[\s>])/i.test(tag)) {
|
|
218
|
+
const typed = globalThis.RapierMarkdownSpec.typography;
|
|
219
|
+
const css = typed.style(typed.parseStyle(md.utils.unescapeAll(String(value || '').replace(/^["']|["']$/g, '').replace(/\\/g, '\\\\'))));
|
|
220
|
+
return css ? ' style="' + md.utils.escapeHtml(css) + '"' : '';
|
|
221
|
+
}
|
|
198
222
|
return key.startsWith('data-') || key === 'for' || key === 'id' && _rapierChromeOwnsId(attributeText(value)) ? '' : whole;
|
|
199
223
|
}) : tag;
|
|
200
224
|
md.renderer.rules.html_inline = (tokens, idx) => cleanTag(tokens[idx].content);
|
|
201
|
-
md.renderer.rules.html_block = (tokens, idx
|
|
202
|
-
tag => env?.rapierDocumentSpace ? cleanTag(tag).replace(/^<([A-Za-z][A-Za-z0-9:-]*)(?=[\s/>])/, '<$1 data-md-html-block="' + idx + '"') : cleanTag(tag), md);
|
|
225
|
+
md.renderer.rules.html_block = (tokens, idx) => _rapierRawHtmlFragment(tokens[idx].content, cleanTag, md);
|
|
203
226
|
}
|
|
227
|
+
// A highlight written in one of the picker's colours is that swatch, as `==🟢x==` is, and follows the theme.
|
|
228
|
+
const inlineHtml = md.renderer.rules.html_inline || ((tokens, idx) => tokens[idx].content);
|
|
229
|
+
md.renderer.rules.html_inline = (tokens, idx, ...rest) => {
|
|
230
|
+
const swatch = /^<mark style="background-color:(#[0-9a-f]{6});?">$/i.exec(tokens[idx].content);
|
|
231
|
+
const colour = swatch && Object.keys(RAPIER_PORTABLE_HIGHLIGHT_HEX).find(name => RAPIER_PORTABLE_HIGHLIGHT_HEX[name] === swatch[1].toLowerCase());
|
|
232
|
+
return colour ? '<mark data-rapier-highlight="' + colour + '">' : inlineHtml(tokens, idx, ...rest);
|
|
233
|
+
};
|
|
204
234
|
|
|
205
235
|
const _ulOpenDefault = md.renderer.rules.bullet_list_open
|
|
206
236
|
|| function (tokens, idx, opts, _env, self) { return self.renderToken(tokens, idx, opts); };
|
|
@@ -293,8 +323,7 @@ function initMarkdownIt() {
|
|
|
293
323
|
'</code> fence. Text below is still inside the block.</p>' + sourceHtml + '</figure>\n';
|
|
294
324
|
}
|
|
295
325
|
const provider = env.rapierProviders === false ? null : _rapierProviders.mermaid;
|
|
296
|
-
const native = globalThis.RapierFlowchart?.parseFlowchart(src).ok === true
|
|
297
|
-
provider?.status === 'ready' && globalThis.RapierDiagrams?.parseDiagram(src).ok === true;
|
|
326
|
+
const native = globalThis.RapierFlowchart?.parseFlowchart(src).ok === true;
|
|
298
327
|
const ready = native || !!(provider && provider.status === 'ready' && typeof provider.renderToString === 'function');
|
|
299
328
|
if (!ready && env.rapierProviders !== false && !_diagramPromptedOnce) {
|
|
300
329
|
_diagramPromptedOnce = true;
|
|
@@ -331,9 +360,9 @@ function initMarkdownIt() {
|
|
|
331
360
|
|
|
332
361
|
{
|
|
333
362
|
let _mathPromptedOnce = false;
|
|
334
|
-
const _renderMath = (src, displayMode, env) => {
|
|
363
|
+
const _renderMath = (src, displayMode, env, authored) => {
|
|
335
364
|
const marker = displayMode ? '$$' : '$';
|
|
336
|
-
const source = marker + src + marker, literal = md.utils.escapeHtml(source);
|
|
365
|
+
const source = authored || marker + src + marker, literal = md.utils.escapeHtml(source);
|
|
337
366
|
if (env.rapierPage) return '<span data-rapier-math-source="">' + literal + '</span>';
|
|
338
367
|
const mathProvider = env.rapierProviders === false ? null : _rapierProviders.math;
|
|
339
368
|
if (!mathProvider || mathProvider.status !== 'ready' || typeof mathProvider.renderToString !== 'function') {
|
|
@@ -345,14 +374,36 @@ function initMarkdownIt() {
|
|
|
345
374
|
+ literal + '</span>';
|
|
346
375
|
}
|
|
347
376
|
try {
|
|
348
|
-
|
|
377
|
+
// The explicit output-only call keeps structured results beside the unchanged SVG
|
|
378
|
+
// islands. MathML never enters editable text projections or source serialization.
|
|
379
|
+
let rendered;
|
|
380
|
+
if (env.rapierMathEquations) {
|
|
381
|
+
const combined = mathProvider.renderWithMathML(src, {displayMode});
|
|
382
|
+
if (!combined || typeof combined.svg !== 'string' || !combined.semantics ||
|
|
383
|
+
typeof combined.semantics !== 'object' || Array.isArray(combined.semantics) || !Array.isArray(combined.errors))
|
|
384
|
+
throw new TypeError('The Rapier Math shared result is invalid.');
|
|
385
|
+
rendered = combined.svg;
|
|
386
|
+
env.rapierMathEquations.push({source, tex: src, displayMode,
|
|
387
|
+
semantics: combined.semantics, errors: combined.errors});
|
|
388
|
+
} else rendered = mathProvider.renderToString(src, {displayMode});
|
|
389
|
+
let encoded;
|
|
390
|
+
try { encoded = encodeURIComponent(source); }
|
|
391
|
+
catch (error) {
|
|
392
|
+
// An ill-formed UTF-16 source cannot enter the URI-encoded edit marker.
|
|
393
|
+
// Keep the ordinary source-only HTML and the collected refusal diagnostic.
|
|
394
|
+
if (!(error instanceof URIError)) throw error;
|
|
395
|
+
return '<span data-rapier-math-source="">' + literal + '</span>';
|
|
396
|
+
}
|
|
349
397
|
return '<span class="math-rendered" data-math-src="'
|
|
350
|
-
+
|
|
398
|
+
+ encoded + '">' + rendered + '</span>';
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
if (env.rapierMathEquations) throw error;
|
|
402
|
+
return '<span data-rapier-math-source="">' + literal + '</span>';
|
|
351
403
|
}
|
|
352
|
-
catch (_) { return '<span data-rapier-math-source="">' + literal + '</span>'; }
|
|
353
404
|
};
|
|
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';
|
|
405
|
+
md.renderer.rules.math_inline = (tokens, idx, _opts, env) => _renderMath(tokens[idx].content, false, env, tokens[idx].meta?.mathSource);
|
|
406
|
+
md.renderer.rules.math_block = (tokens, idx, _opts, env) => wrapDisplayMath(_renderMath(tokens[idx].content, true, env, tokens[idx].meta?.mathSource)) + '\n';
|
|
356
407
|
}
|
|
357
408
|
globalThis.RapierMarkdownLayout.installMarkdownLayout(md);
|
|
358
409
|
globalThis.RapierImageAssets.configureParser(md, _rapierSplitOpeningFrontmatter);
|
|
@@ -378,9 +429,10 @@ function _rapierRenderSemanticRoot(canonical, metadata, options = {}) {
|
|
|
378
429
|
const markdown = _rapierSplitOpeningFrontmatter(canonical).body;
|
|
379
430
|
// Source-semantic output is independent of installed rendering providers. Complete
|
|
380
431
|
// artifacts keep the default provider path and materialize their visual content.
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
432
|
+
// A portable projection keeps natural sizing. Live readers opt into the same transient
|
|
433
|
+
// AUTO policy as the editor; neither a saved page nor its source inherits that choice.
|
|
434
|
+
const env = {rapierImagePresentation: options.imagePresentation === true, ...(options.page ? {rapierPage: true} : {}), ...(options.providers === false ? {rapierProviders: false} : {})};
|
|
435
|
+
if (options.mathEquations) env.rapierMathEquations = options.mathEquations;
|
|
384
436
|
let rendered = md
|
|
385
437
|
? md.render(markdown, env)
|
|
386
438
|
: '<pre><code>' + escapeRapierHtmlText(markdown) + '</code></pre>';
|
|
@@ -398,7 +450,7 @@ function _rapierRenderSemanticRoot(canonical, metadata, options = {}) {
|
|
|
398
450
|
root.querySelectorAll('pre > code').forEach(_rapierNormalizeCodeElement);
|
|
399
451
|
_rapierMarkTableCaptions(root);
|
|
400
452
|
_rapierMarkFigureCaptions(root);
|
|
401
|
-
_rapierMarkListContinuations(
|
|
453
|
+
_rapierMarkListContinuations(_rapierChildElements(root));
|
|
402
454
|
_rapierDecorateJumpLists(root);
|
|
403
455
|
return root;
|
|
404
456
|
}
|
|
@@ -413,20 +465,21 @@ async function _rapierRenderSemanticRootAsync(canonical, metadata, options = {})
|
|
|
413
465
|
}
|
|
414
466
|
const markdown = _rapierSplitOpeningFrontmatter(canonical).body;
|
|
415
467
|
const fallbackEnv = options.page ? {rapierPage: true} : {};
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
env
|
|
468
|
+
let parsed = options.parsed || (runtime.parseAsync ? await runtime.parseAsync(markdown, options) : {tokens: md.parse(markdown, fallbackEnv), env: fallbackEnv});
|
|
469
|
+
let {tokens, env} = parsed, labels = env.footnotes?.list?.map((item, index) => String(item?.label || ('inline-' + (index + 1))));
|
|
470
|
+
env = {...env, rapierImagePresentation: options.imagePresentation === true};
|
|
471
|
+
await pause(work, .3);
|
|
419
472
|
const root = document.createElement('div'), part = document.createElement('div'), boundary = htmlBoundary();
|
|
420
473
|
root.setAttribute('data-rapier-semantic-root', 'true');
|
|
421
|
-
|
|
422
|
-
let html = '', began = performance.now();
|
|
474
|
+
let html = '', began = performance.now(), complete = false;
|
|
423
475
|
const append = () => {
|
|
424
476
|
if (md.renderer.rapierTransformHtml) html = md.renderer.rapierTransformHtml(html);
|
|
425
477
|
if (labels?.length) html = _relabelFootnoteHtml(html, labels);
|
|
426
|
-
part
|
|
478
|
+
admitHtml(part, html, sanitizeRapierHtml, runtime.sanitizeRapierFragment, options.page ? 'source' : 'export');
|
|
427
479
|
while (part.firstChild) root.appendChild(part.firstChild);
|
|
428
480
|
html = '';
|
|
429
481
|
};
|
|
482
|
+
try {
|
|
430
483
|
for (let index = 0; index < tokens.length; index++) {
|
|
431
484
|
const token = tokens[index], rule = md.renderer.rules[token.type];
|
|
432
485
|
let rendered = token.type === 'inline' ? md.renderer.renderInline(token.children, md.options, env)
|
|
@@ -439,9 +492,15 @@ async function _rapierRenderSemanticRootAsync(canonical, metadata, options = {})
|
|
|
439
492
|
}
|
|
440
493
|
}
|
|
441
494
|
if (html) append();
|
|
495
|
+
// Token/environment objects are no longer inputs once their complete HTML
|
|
496
|
+
// is admitted. Drop our references before the document-wide DOM passes;
|
|
497
|
+
// never mutate an optional parsed result retained by another caller.
|
|
498
|
+
parsed = tokens = env = labels = null;
|
|
442
499
|
await finishAsync(_rapierSemanticDecorations(root), work);
|
|
443
500
|
await pause(work, .6);
|
|
501
|
+
complete = true;
|
|
444
502
|
return root;
|
|
503
|
+
} finally { if (!complete) { root.replaceChildren(); part.replaceChildren(); } }
|
|
445
504
|
}
|
|
446
505
|
|
|
447
506
|
function* _rapierSemanticDecorations(root) {
|
|
@@ -451,7 +510,7 @@ function* _rapierSemanticDecorations(root) {
|
|
|
451
510
|
for (const code of root.querySelectorAll('pre > code')) { _rapierNormalizeCodeElement(code); yield; }
|
|
452
511
|
yield* _rapierMarkTableCaptionsSteps(root); yield;
|
|
453
512
|
yield* _rapierMarkFigureCaptionsSteps(root); yield;
|
|
454
|
-
yield* _rapierMarkListContinuationsSteps(
|
|
513
|
+
yield* _rapierMarkListContinuationsSteps(_rapierChildElements(root)); yield;
|
|
455
514
|
yield* _rapierDecorateJumpListsSteps(root); yield;
|
|
456
515
|
}
|
|
457
516
|
|
|
@@ -630,16 +689,81 @@ function _rapierImageAltText(value, env = {}) {
|
|
|
630
689
|
return md.renderer.renderInlineAsText(tokens[0]?.children || [], md.options, env);
|
|
631
690
|
}
|
|
632
691
|
|
|
633
|
-
|
|
692
|
+
// Intrinsic facts are a presentation input, never an authored width or a resource admission.
|
|
693
|
+
// Contain both edges within the column, sharing a 1.6-column sum-of-edges budget.
|
|
694
|
+
// Reciprocal portraits/landscapes have equal weight; the native-width cap never enlarges art.
|
|
695
|
+
function _rapierImageGeometryFacts(geometry, automatic) {
|
|
696
|
+
const w = Number(geometry?.width), h = Number(geometry?.height);
|
|
697
|
+
const known = Number.isFinite(w) && Number.isFinite(h) && w > 0 && h > 0 && Number.isFinite(w / h) && w / h > 0;
|
|
698
|
+
const width = known ? w : 0, height = known ? h : 0;
|
|
699
|
+
const styles = known ? [['aspect-ratio', width + '/' + height]] : automatic ? [['aspect-ratio', '1/1']] : [];
|
|
700
|
+
if (automatic) {
|
|
701
|
+
const ratio = known ? width / height : 1;
|
|
702
|
+
const share = Math.min(1, ratio, 1.6 / (1 + 1 / ratio));
|
|
703
|
+
styles.push(['--rapier-image-auto-width', Number((share * 100).toFixed(6)) + '%'],
|
|
704
|
+
['--rapier-image-natural-width', known ? width + 'px' : '100%']);
|
|
705
|
+
}
|
|
706
|
+
return {width, height, styles};
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// Native loads supply their current element's dimensions synchronously. Embedded images
|
|
710
|
+
// supply an admitted row only after their existing document/resource ticket checks.
|
|
711
|
+
function _rapierApplyImageGeometry(image, geometry) {
|
|
712
|
+
if (image?.tagName !== 'IMG' || image.hasAttribute('data-rapier-html')) return false;
|
|
713
|
+
const mode = image.getAttribute('data-rapier-image-geometry');
|
|
714
|
+
if (!['auto', 'sized', 'natural'].includes(mode)) return false;
|
|
715
|
+
const native = geometry === undefined;
|
|
716
|
+
if (native) {
|
|
717
|
+
if (!image.isConnected || !image.complete || image.hasAttribute('data-rapier-asset') || image.hasAttribute('data-rapier-image-url')) return false;
|
|
718
|
+
geometry = {width: image.naturalWidth, height: image.naturalHeight};
|
|
719
|
+
}
|
|
720
|
+
const facts = _rapierImageGeometryFacts(geometry, mode === 'auto');
|
|
721
|
+
if (!facts.width || !facts.height) return false;
|
|
722
|
+
const attributes = [
|
|
723
|
+
['data-rapier-natural-width', String(facts.width)], ['data-rapier-natural-height', String(facts.height)],
|
|
724
|
+
...(!image.hasAttribute('data-rapier-image-size') ? [['width', String(facts.width)]] : []), ['height', String(facts.height)],
|
|
725
|
+
].filter(([name, value]) => image.getAttribute(name) !== value);
|
|
726
|
+
const styles = facts.styles.filter(([name, value]) => (name === 'aspect-ratio'
|
|
727
|
+
? image.style.getPropertyValue(name).replace(/\s/g, '') : image.style.getPropertyValue(name)) !== value);
|
|
728
|
+
// Ordinary mutations revisit mounted images. Settled facts need no source-URL
|
|
729
|
+
// read or parse (which can be the entire inline image), nor any DOM writes.
|
|
730
|
+
if (!attributes.length && !styles.length) return false;
|
|
731
|
+
if (native) {
|
|
732
|
+
const src = image.getAttribute('src'), current = image.currentSrc;
|
|
733
|
+
if (!src || !current) return false;
|
|
734
|
+
try { if (src !== current && new URL(src, image.ownerDocument.baseURI).href !== current) return false; }
|
|
735
|
+
catch (_) { return false; }
|
|
736
|
+
}
|
|
737
|
+
for (const [name, value] of attributes) image.setAttribute(name, value);
|
|
738
|
+
for (const [name, value] of styles) image.style.setProperty(name, value);
|
|
739
|
+
return true;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
function _rapierImageSizeAttributes(size, marker, geometry, automatic = true) {
|
|
634
743
|
const layout = globalThis.RapierMarkdownLayout.parseLayout(marker || '');
|
|
635
744
|
// Plain (rung 0, docs/standard-adoption.md): every picture renders at its natural width, the
|
|
636
745
|
// same as a reader with no layout facts at all -- Obsidian's `![alt|N]` alt-text width (the
|
|
637
746
|
// `size` fallback below) is a different, established convention and is unaffected.
|
|
638
|
-
|
|
639
|
-
|
|
747
|
+
let style = layout && !_rapierPlainLayout() ? globalThis.RapierMarkdownLayout.imageStyle(layout) : '';
|
|
748
|
+
let attributes = '';
|
|
749
|
+
if (layout?.width != null && style) attributes = ' data-md-image-width="' + layout.width + '"';
|
|
640
750
|
// A fade rides beside any width, the alt-text's own included.
|
|
641
|
-
|
|
642
|
-
'
|
|
751
|
+
else if (size) {
|
|
752
|
+
attributes = ' width="' + size.width + '" data-rapier-image-size="' + size.width + '"';
|
|
753
|
+
style = '--md-image-width:' + size.width + 'px' + (style ? ';' + style : '');
|
|
754
|
+
}
|
|
755
|
+
if (geometry !== undefined) {
|
|
756
|
+
const mode = size || layout?.width != null || layout?.lines != null ? 'sized' : automatic ? 'auto' : 'natural';
|
|
757
|
+
const facts = _rapierImageGeometryFacts(geometry, mode === 'auto');
|
|
758
|
+
attributes += ' data-rapier-image-geometry="' + mode + '"';
|
|
759
|
+
if (facts.width) {
|
|
760
|
+
attributes += ' data-rapier-natural-width="' + facts.width + '" data-rapier-natural-height="' + facts.height + '"';
|
|
761
|
+
if (!size || !attributes.includes(' width=')) attributes += ' width="' + facts.width + '"';
|
|
762
|
+
attributes += ' height="' + facts.height + '"';
|
|
763
|
+
}
|
|
764
|
+
style = [...facts.styles.map(([name, value]) => name + ':' + value), ...(style ? [style] : [])].join(';');
|
|
765
|
+
}
|
|
766
|
+
return attributes + (style ? ' style="' + style + '"' : '');
|
|
643
767
|
}
|
|
644
768
|
|
|
645
769
|
function _rapierRawHtmlFragment(source, rewrite, parser) {
|
|
@@ -660,13 +784,20 @@ function _rapierApplyBlockDirection(root) { return finish(_rapierApplyBlockDirec
|
|
|
660
784
|
|
|
661
785
|
function* _rapierApplyBlockDirectionSteps(root) {
|
|
662
786
|
if (!root || typeof root.querySelectorAll !== 'function') return;
|
|
663
|
-
|
|
787
|
+
const blocks = new Set(_RAPIER_DIR_BLOCKS.split(',')), elements = root.ownerDocument.createTreeWalker(root, 1);
|
|
788
|
+
for (let el = elements.nextNode(); el; el = elements.nextNode()) {
|
|
664
789
|
yield;
|
|
665
|
-
if (el.
|
|
790
|
+
if (!blocks.has(el.localName)) continue;
|
|
791
|
+
let literal = false;
|
|
792
|
+
for (let parent = el.parentElement; parent; parent = parent.parentElement)
|
|
793
|
+
if (parent.localName === 'pre' || parent.localName === 'code') { literal = true; break; }
|
|
794
|
+
if (literal) continue;
|
|
666
795
|
let dir = 'ltr', found = false;
|
|
667
796
|
const walker = el.ownerDocument.createTreeWalker(el, 4);
|
|
797
|
+
let scanned = 0;
|
|
668
798
|
for (let text = walker.nextNode(); text && !found; text = walker.nextNode()) {
|
|
669
799
|
for (const character of text.nodeValue || '') {
|
|
800
|
+
if (++scanned % 1024 === 0) yield;
|
|
670
801
|
const strong = _rapierBidiStrong(character.codePointAt(0));
|
|
671
802
|
if (strong) { dir = strong === 'R' ? 'rtl' : 'ltr'; found = true; break; }
|
|
672
803
|
}
|
|
@@ -826,7 +957,9 @@ function* _rapierDecorateJumpListsSteps(root) {
|
|
|
826
957
|
}
|
|
827
958
|
}
|
|
828
959
|
|
|
829
|
-
|
|
960
|
+
// Do not retain a live collection on the root that Copy will consume. Some
|
|
961
|
+
// DOM hosts eagerly refresh it on every later removal of a complete unit.
|
|
962
|
+
const first = root.firstElementChild, only = first && !first.nextElementSibling ? first : null;
|
|
830
963
|
if (only && only.tagName === 'P' && /^jump\s+to:?$/i.test(String(only.textContent || '').trim())) {
|
|
831
964
|
only.classList.add('rapier-jump-label-candidate');
|
|
832
965
|
}
|
|
@@ -849,20 +982,23 @@ function* _rapierMarkFigureCaptionsSteps(root) {
|
|
|
849
982
|
|
|
850
983
|
function _rapierMarkListContinuations(blocks) { return finish(_rapierMarkListContinuationsSteps(blocks)); }
|
|
851
984
|
|
|
985
|
+
function* _rapierChildElements(root) {
|
|
986
|
+
for (let child = root.firstElementChild; child; child = child.nextElementSibling) yield child;
|
|
987
|
+
}
|
|
988
|
+
|
|
852
989
|
function* _rapierMarkListContinuationsSteps(blocks) {
|
|
853
990
|
const isHeading = element => !!element && /^H[1-6]$/.test(element.tagName);
|
|
854
|
-
|
|
855
|
-
|
|
991
|
+
let previous = null, headings = [], last = 0;
|
|
992
|
+
for (const element of blocks) { yield;
|
|
993
|
+
if (previous && isHeading(element)) {headings.push(element); continue;}
|
|
994
|
+
if (!element || element.tagName !== 'OL') {previous = null; headings = []; continue;}
|
|
856
995
|
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', '');}
|
|
996
|
+
if (previous && headings.length && start === last + 1) {
|
|
997
|
+
previous.setAttribute('data-rapier-list-continues', '');
|
|
998
|
+
for (const heading of headings) { yield;heading.setAttribute('data-rapier-list-through', '');}
|
|
999
|
+
}
|
|
1000
|
+
previous = element; headings = [];
|
|
1001
|
+
last = (Number.isFinite(start) ? start : 1) + element.querySelectorAll(':scope > li').length - 1;
|
|
866
1002
|
}
|
|
867
1003
|
}
|
|
868
1004
|
|
|
@@ -1031,6 +1167,11 @@ function _rapierProjectPortableRoot(semanticRoot, options) { return finish(_rapi
|
|
|
1031
1167
|
async function _rapierProjectPortableRootAsync(semanticRoot, options = {}) { return finishAsync(_rapierProjectPortableRootSteps(semanticRoot, options), options.work); }
|
|
1032
1168
|
|
|
1033
1169
|
function* _rapierProjectPortableRootSteps(semanticRoot, options) {
|
|
1170
|
+
const root = yield* _rapierProjectPortableNodesSteps(semanticRoot, options);
|
|
1171
|
+
return yield* cleanTree(root, sanitizeRapierHtml, 'export', runtime.sanitizeRapierFragment);
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
function* _rapierProjectPortableNodesSteps(semanticRoot, options) {
|
|
1034
1175
|
const opts = options || {};
|
|
1035
1176
|
// This projection is HTML-shaped (width baked into a CSS `style`, `data-*` stripped) for the
|
|
1036
1177
|
// consumers that read HTML -- publishing/fragment export, TXT. DOCX is not an HTML consumer: its
|
|
@@ -1040,7 +1181,9 @@ function* _rapierProjectPortableRootSteps(semanticRoot, options) {
|
|
|
1040
1181
|
// the semantic root carries them. Ordinary paragraph/list CSS measurements also reach their
|
|
1041
1182
|
// native reader; no derived private CSS is needed.
|
|
1042
1183
|
const keepLayoutData = !!opts.keepLayoutData;
|
|
1043
|
-
|
|
1184
|
+
// Copy owns its freshly rendered, detached root. Other callers retain their
|
|
1185
|
+
// semantic tree and receive the usual independent portable projection.
|
|
1186
|
+
const root = opts.inPlace ? semanticRoot : yield* cloneTree(semanticRoot), nativeElements = new Set();
|
|
1044
1187
|
// A source soft break is a reading space, never the editor's invisible token.
|
|
1045
1188
|
yield* _rapierResolveSoftBreakTokensSteps(root);
|
|
1046
1189
|
// Word consumes these source-bearing islands as pictures with TeX alternatives. Keep
|
|
@@ -1148,15 +1291,16 @@ function* _rapierProjectPortableRootSteps(semanticRoot, options) {
|
|
|
1148
1291
|
const imageLayout = element.tagName === 'IMG' ? globalThis.RapierMarkdownLayout.parseLayoutAttribute(element.getAttribute('data-rapier-image-layout')) : null;
|
|
1149
1292
|
const measurements = keepLayoutData && /^(?:P|H[1-6]|LI|OL|UL)$/.test(element.tagName)
|
|
1150
1293
|
? ['text-indent', 'margin-inline-start'].map(property => [property, element.style.getPropertyValue(property)]) : [];
|
|
1294
|
+
// The native table reader admits automatic hyphenation from ordinary paragraph CSS.
|
|
1295
|
+
if (keepLayoutData && element.tagName === 'P' && element.closest('table') && element.style.getPropertyValue('hyphens').trim() === 'auto')
|
|
1296
|
+
measurements.push(['hyphens', 'auto']);
|
|
1151
1297
|
if (keepLayoutData && element.closest('pre')) for (const property of ['color', 'font-weight', 'font-style'])
|
|
1152
1298
|
measurements.push([property, element.style.getPropertyValue(property)]);
|
|
1153
1299
|
if (keepLayoutData && element.tagName === 'BLOCKQUOTE')
|
|
1154
1300
|
measurements.push(['border-inline-start-color', element.style.borderInlineStartColor]);
|
|
1155
1301
|
const runTypography = keepLayoutData ? ['font-family', 'font-variant-caps'].map(property => [property, element.style.getPropertyValue(property)]) : [];
|
|
1156
1302
|
|
|
1157
|
-
const
|
|
1158
|
-
const fontFamily = element.tagName === 'SPAN' ? element.getAttribute('data-md-font') : null;
|
|
1159
|
-
const fontCss = fontFamily ? globalThis.RapierMarkdownSpec.fontFamilyCss(fontFamily) : '';
|
|
1303
|
+
const typed = globalThis.RapierMarkdownSpec.typography, typography = typed.nodeStyle(element);
|
|
1160
1304
|
|
|
1161
1305
|
const highlightHex = element.tagName === 'MARK'
|
|
1162
1306
|
? (RAPIER_PORTABLE_HIGHLIGHT_HEX[String(element.getAttribute('data-rapier-highlight') || '').toLowerCase()] || RAPIER_PORTABLE_HIGHLIGHT_HEX.yellow)
|
|
@@ -1167,9 +1311,14 @@ function* _rapierProjectPortableRootSteps(semanticRoot, options) {
|
|
|
1167
1311
|
const isPageBreak = element.hasAttribute('data-md-break');
|
|
1168
1312
|
for (const attribute of Array.from(element.attributes)) { yield;
|
|
1169
1313
|
const name = attribute.name.toLowerCase();
|
|
1314
|
+
// Copy reads these inert words from the final admitted nodes, then removes
|
|
1315
|
+
// them before emitting HTML. They grant no rendering or resource authority.
|
|
1316
|
+
if (opts.heldPictures && /^(?:SPAN|IMG)$/.test(element.tagName) &&
|
|
1317
|
+
(name === 'data-rapier-remote-src' || name === 'data-rapier-remote-alt')) continue;
|
|
1170
1318
|
if (keepLayoutData && (name === 'data-rapier-image-layout' || name === 'data-md-layout' || name === 'data-md-align' || name === 'data-rapier-ink'
|
|
1171
1319
|
|| name === 'data-md-first-line-end' || name === 'data-md-initial-font' || name === 'data-md-initial-size'
|
|
1172
|
-
|| name === 'data-md-picture-cap-height' || name === 'data-md-picture-cap-top'
|
|
1320
|
+
|| name === 'data-md-picture-cap-height' || name === 'data-md-picture-cap-top' || name === 'data-rapier-highlight' || name === 'data-rapier-animation'
|
|
1321
|
+
|| name === 'data-rapier-list-ordinal' || name === 'data-rapier-list-delimiter' || name === 'data-md-layout-tight')) continue;
|
|
1173
1322
|
if (name === 'class' || name === 'style' || name.startsWith('data-')
|
|
1174
1323
|
|| name === 'contenteditable' || name === 'spellcheck'
|
|
1175
1324
|
|| name === 'tabindex' || name === 'disabled' || name === 'aria-disabled') {
|
|
@@ -1192,10 +1341,12 @@ function* _rapierProjectPortableRootSteps(semanticRoot, options) {
|
|
|
1192
1341
|
if (imageLayout.width != null) element.removeAttribute('width');
|
|
1193
1342
|
if (pictureStyle) element.style.cssText = pictureStyle;
|
|
1194
1343
|
}
|
|
1195
|
-
if (colorHex && /^#[0-9a-f]{6}$/i.test(colorHex)) element.style.color = colorHex.toLowerCase();
|
|
1196
|
-
if (fontCss) element.style.fontFamily = fontCss;
|
|
1197
1344
|
if (highlightHex) element.style.backgroundColor = highlightHex;
|
|
1198
1345
|
if (isTableCaption) { element.style.fontStyle = 'italic'; element.style.color = '#555555'; }
|
|
1346
|
+
// Already validated facts serialize directly. An inert host's partial CSSOM must
|
|
1347
|
+
// not silently discard a quoted family or round an authored decimal.
|
|
1348
|
+
const css = typed.style(typography);
|
|
1349
|
+
if (css) element.setAttribute('style', (element.getAttribute('style') ? element.getAttribute('style') + ';' : '') + css);
|
|
1199
1350
|
|
|
1200
1351
|
if (isPageBreak) {
|
|
1201
1352
|
const p = document.createElement('p');
|
|
@@ -1210,7 +1361,7 @@ function* _rapierProjectPortableRootSteps(semanticRoot, options) {
|
|
|
1210
1361
|
literal.style.setProperty('hanging-punctuation', 'none');
|
|
1211
1362
|
}
|
|
1212
1363
|
|
|
1213
|
-
return
|
|
1364
|
+
return root;
|
|
1214
1365
|
}
|
|
1215
1366
|
|
|
1216
1367
|
function _rapierPortableHtml(root) {
|
|
@@ -1218,6 +1369,69 @@ function _rapierPortableHtml(root) {
|
|
|
1218
1369
|
}
|
|
1219
1370
|
async function _rapierPortableHtmlAsync(root, work) { return finishAsync(serializeTree(root, sanitizeRapierHtml, true), work); }
|
|
1220
1371
|
|
|
1372
|
+
// One captured document owns this pipeline and its detached nodes. Global semantic
|
|
1373
|
+
// facts are resolved before projection. Final admission consumes the old tree a
|
|
1374
|
+
// complete fragment at a time; HTML and words then share that admitted tree.
|
|
1375
|
+
async function _rapierClipboardPayloadAsync(canonical, metadata, options = {}) {
|
|
1376
|
+
const work = options.work;
|
|
1377
|
+
let semantic;
|
|
1378
|
+
const part = document.createElement('div');
|
|
1379
|
+
try {
|
|
1380
|
+
semantic = await _rapierRenderSemanticRootAsync(canonical, metadata, {work});
|
|
1381
|
+
if (options.formatted && options.materialize && await options.materialize(semantic) === false) return null;
|
|
1382
|
+
check(work);
|
|
1383
|
+
await finishAsync(_rapierProjectPortableNodesSteps(semantic,
|
|
1384
|
+
{baseName: options.baseName, inPlace: true, heldPictures: true}), work);
|
|
1385
|
+
const words = metadata.docKind === 'markdown' ? _rapierPortableTextWriter({heldPictures: true}) : null;
|
|
1386
|
+
const parts = [];
|
|
1387
|
+
let pictures = false;
|
|
1388
|
+
const plain = await finishAsync((function* () {
|
|
1389
|
+
for (const html of htmlParts(semantic, 16384, true)) {
|
|
1390
|
+
if (html === null) { yield; continue; }
|
|
1391
|
+
// Reparse the complete transformed unit at the existing final export
|
|
1392
|
+
// boundary. Both formats consume these admitted nodes before release.
|
|
1393
|
+
admitHtml(part, html, sanitizeRapierHtml, runtime.sanitizeRapierFragment);
|
|
1394
|
+
yield;
|
|
1395
|
+
if (words) yield* words.write(part);
|
|
1396
|
+
if (options.formatted) {
|
|
1397
|
+
for (const node of part.querySelectorAll('[data-rapier-remote-src],[data-rapier-remote-alt]')) {
|
|
1398
|
+
node.removeAttribute('data-rapier-remote-src'); node.removeAttribute('data-rapier-remote-alt'); yield;
|
|
1399
|
+
}
|
|
1400
|
+
pictures ||= !!part.querySelector('img[src^="data:"]');
|
|
1401
|
+
parts.push(yield* serializeTree(part));
|
|
1402
|
+
}
|
|
1403
|
+
part.replaceChildren(); yield;
|
|
1404
|
+
}
|
|
1405
|
+
return words ? yield* words.finish() : canonical;
|
|
1406
|
+
})(), work);
|
|
1407
|
+
check(work);
|
|
1408
|
+
if (!options.formatted) return {plain};
|
|
1409
|
+
return {plain, html: parts.join('').trim(), withoutPictures: pictures ? async () => {
|
|
1410
|
+
// Only this writer's finished HTML reaches this closure. On refusal,
|
|
1411
|
+
// reconstruct one complete fragment, change pictures to text and admit
|
|
1412
|
+
// that changed output. No full fallback DOM survives the operation.
|
|
1413
|
+
return finishAsync((function* () {
|
|
1414
|
+
const output = [], part = document.createElement('div');
|
|
1415
|
+
try {
|
|
1416
|
+
for (const html of parts) {
|
|
1417
|
+
if (!html.includes('<img')) { output.push(html); yield; continue; }
|
|
1418
|
+
part.innerHTML = html;
|
|
1419
|
+
let changed = false;
|
|
1420
|
+
for (const image of part.querySelectorAll('img[src^="data:"]')) {
|
|
1421
|
+
const replacement = document.createElement('span');
|
|
1422
|
+
replacement.textContent = '[image: ' + (String(image.getAttribute('alt') || 'image').trim() || 'image') + ']';
|
|
1423
|
+
image.replaceWith(replacement); changed = true; yield;
|
|
1424
|
+
}
|
|
1425
|
+
output.push(changed ? yield* serializeTree(part, sanitizeRapierHtml) : html);
|
|
1426
|
+
part.replaceChildren(); yield;
|
|
1427
|
+
}
|
|
1428
|
+
return output.join('').trim();
|
|
1429
|
+
} finally { part.replaceChildren(); }
|
|
1430
|
+
})(), work);
|
|
1431
|
+
} : null};
|
|
1432
|
+
} finally { semantic?.replaceChildren(); part.replaceChildren(); }
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1221
1435
|
function _rapierNormalizeInlineText(value) {
|
|
1222
1436
|
return String(value || '')
|
|
1223
1437
|
.replace(/\u00a0/g, ' ')
|
|
@@ -1245,79 +1459,102 @@ function* _rapierHeldPicturesAsWordsSteps(root) {
|
|
|
1245
1459
|
return copy;
|
|
1246
1460
|
}
|
|
1247
1461
|
|
|
1248
|
-
function _rapierPortableInlineText(node, options) {
|
|
1462
|
+
function _rapierPortableInlineText(node, options) { return finish(_rapierPortableInlineTextSteps(node, options)); }
|
|
1463
|
+
|
|
1464
|
+
function* _rapierPortableInlineTextSteps(node, options) {
|
|
1249
1465
|
const opts = options || {};
|
|
1250
1466
|
if (!node) return '';
|
|
1467
|
+
yield;
|
|
1251
1468
|
if (node.nodeType === Node.TEXT_NODE) return node.nodeValue || '';
|
|
1252
1469
|
if (node.nodeType !== Node.ELEMENT_NODE) return '';
|
|
1253
1470
|
const tag = node.tagName;
|
|
1471
|
+
if (opts.heldPictures && /^(?:SPAN|IMG)$/.test(tag) && node.hasAttribute('data-rapier-remote-src'))
|
|
1472
|
+
return _rapierPictureWords(String(node.getAttribute('data-rapier-remote-alt') || '').trim(),
|
|
1473
|
+
String(node.getAttribute('data-rapier-remote-src') || '').trim());
|
|
1254
1474
|
if (tag === 'BR') return '\n';
|
|
1255
1475
|
// A task's box is what the person sees of it.
|
|
1256
1476
|
if (tag === 'INPUT') return /^checkbox$/i.test(node.type || '') ? (node.checked || node.hasAttribute('checked') ? '☑ ' : '☐ ') : '';
|
|
1257
1477
|
if (tag === 'IMG') {
|
|
1258
1478
|
if (opts.visible) return '';
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1479
|
+
return _rapierPictureWords(String(node.getAttribute('alt') || '').trim(), String(node.getAttribute('src') || '').trim());
|
|
1480
|
+
}
|
|
1481
|
+
if (tag === 'CODE' && !opts.inPre) {
|
|
1482
|
+
const source = String(node.textContent || '');
|
|
1483
|
+
return opts.visible ? source : source ? '`' + source + '`' : '';
|
|
1262
1484
|
}
|
|
1485
|
+
const parts = [];
|
|
1486
|
+
for (let child = node.firstChild; child; child = child.nextSibling) parts.push(yield* _rapierPortableInlineTextSteps(child, opts));
|
|
1487
|
+
const text = parts.join('');
|
|
1263
1488
|
if (tag === 'A') {
|
|
1264
|
-
const label = _rapierNormalizeInlineText(
|
|
1265
|
-
.map(child => _rapierPortableInlineText(child, opts)).join(''));
|
|
1266
|
-
const href = String(node.getAttribute('href') || '').trim();
|
|
1489
|
+
const label = _rapierNormalizeInlineText(text), href = String(node.getAttribute('href') || '').trim();
|
|
1267
1490
|
if (opts.visible || !href || href.charAt(0) === '#') return label;
|
|
1268
1491
|
if (!label || label === href) return href;
|
|
1269
1492
|
return label + ' (' + href + ')';
|
|
1270
1493
|
}
|
|
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('');
|
|
1494
|
+
return text;
|
|
1277
1495
|
}
|
|
1278
1496
|
|
|
1279
|
-
function _rapierPortableListText(list, depth, options) {
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
const ordered = list.tagName === 'OL';
|
|
1497
|
+
function _rapierPortableListText(list, depth, options) { return finish(_rapierPortableListTextSteps(list, depth, options)); }
|
|
1498
|
+
|
|
1499
|
+
function* _rapierPortableListTextSteps(list, depth, options) {
|
|
1500
|
+
const opts = options || {}, level = Math.max(0, depth | 0), ordered = list.tagName === 'OL';
|
|
1283
1501
|
let counter = ordered ? (parseInt(list.getAttribute('start'), 10) || 1) : 0;
|
|
1284
1502
|
const lines = [];
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1503
|
+
for (let item = list.firstElementChild; item; item = item.nextElementSibling) {
|
|
1504
|
+
yield;
|
|
1505
|
+
if (item.tagName !== 'LI') continue;
|
|
1506
|
+
const nested = [], content = [];
|
|
1507
|
+
for (let child = item.firstChild; child; child = child.nextSibling) {
|
|
1508
|
+
if (child.nodeType === Node.ELEMENT_NODE && (child.tagName === 'UL' || child.tagName === 'OL')) nested.push(child);
|
|
1509
|
+
else content.push(yield* _rapierPortableInlineTextSteps(child, opts));
|
|
1510
|
+
}
|
|
1511
|
+
const normalized = _rapierNormalizeInlineText(content.join('')), indent = ' '.repeat(level);
|
|
1292
1512
|
// A task shows its box where a bullet would be.
|
|
1293
1513
|
const marker = /^[☐☑☒]/.test(normalized) ? '' : ordered ? (counter++) + '. ' : '• ';
|
|
1294
|
-
const parts =
|
|
1514
|
+
const parts = normalized.split('\n');
|
|
1295
1515
|
lines.push(indent + marker + (parts.shift() || ''));
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
const
|
|
1299
|
-
if (
|
|
1300
|
-
}
|
|
1301
|
-
}
|
|
1516
|
+
for (const part of parts) { lines.push(indent + ' ' + part); yield; }
|
|
1517
|
+
for (const child of nested) {
|
|
1518
|
+
const text = yield* _rapierPortableListTextSteps(child, level + 1, opts);
|
|
1519
|
+
if (text) lines.push(text);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1302
1522
|
return lines.join('\n');
|
|
1303
1523
|
}
|
|
1304
1524
|
|
|
1305
1525
|
// 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
|
-
|
|
1526
|
+
function _rapierPortableTableText(table, options) { return finish(_rapierPortableTableTextSteps(table, options)); }
|
|
1527
|
+
|
|
1528
|
+
function* _rapierPortableTableTextSteps(table, options) {
|
|
1529
|
+
const opts = options || {}, rows = [];
|
|
1530
|
+
const walker = document.createTreeWalker(table, 1 /* SHOW_ELEMENT */);
|
|
1531
|
+
let row = table.tagName === 'TR' ? table : walker.nextNode();
|
|
1532
|
+
while (row) {
|
|
1533
|
+
yield;
|
|
1534
|
+
if (row.tagName === 'TR') {
|
|
1535
|
+
const cells = [];
|
|
1536
|
+
for (let cell = row.firstElementChild; cell; cell = cell.nextElementSibling) {
|
|
1537
|
+
if (cell.tagName === 'TH' || cell.tagName === 'TD')
|
|
1538
|
+
cells.push(_rapierNormalizeInlineText(yield* _rapierPortableInlineTextSteps(cell, opts)));
|
|
1539
|
+
}
|
|
1540
|
+
const text = cells.join(opts.visible ? '\t' : ' | ');
|
|
1541
|
+
if (text) rows.push(text);
|
|
1542
|
+
}
|
|
1543
|
+
row = table.tagName === 'TR' ? null : walker.nextNode();
|
|
1544
|
+
}
|
|
1545
|
+
return rows.join('\n');
|
|
1315
1546
|
}
|
|
1316
1547
|
|
|
1317
1548
|
function _rapierPortablePlainText(root, options) { return finish(_rapierPortablePlainTextSteps(root, options)); }
|
|
1318
1549
|
async function _rapierPortablePlainTextAsync(root, options = {}) { return finishAsync(_rapierPortablePlainTextSteps(root, options), options.work); }
|
|
1319
1550
|
|
|
1320
1551
|
function* _rapierPortablePlainTextSteps(root, options) {
|
|
1552
|
+
const writer = _rapierPortableTextWriter(options);
|
|
1553
|
+
yield* writer.write(root);
|
|
1554
|
+
return yield* writer.finish();
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
function _rapierPortableTextWriter(options) {
|
|
1321
1558
|
const opts = options || {};
|
|
1322
1559
|
const blocks = [];
|
|
1323
1560
|
const blockTags = new Set([
|
|
@@ -1332,49 +1569,48 @@ function* _rapierPortablePlainTextSteps(root, options) {
|
|
|
1332
1569
|
if (value) blocks.push({ text: value, preserve: !!preserve });
|
|
1333
1570
|
};
|
|
1334
1571
|
|
|
1335
|
-
const
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1572
|
+
const state = () => ({inline: [], table: null});
|
|
1573
|
+
const flush = current => {
|
|
1574
|
+
if (current.inline.length) {
|
|
1575
|
+
pushBlock(_rapierNormalizeInlineText(current.inline.join('')), false);
|
|
1576
|
+
current.inline = [];
|
|
1577
|
+
}
|
|
1578
|
+
if (current.table) {
|
|
1579
|
+
pushBlock(current.table.filter(Boolean).join('\n'), false);
|
|
1580
|
+
current.table = null;
|
|
1581
|
+
}
|
|
1582
|
+
};
|
|
1583
|
+
const collectChildren = function* (parent, current = state(), complete = true) {
|
|
1342
1584
|
const tablePart = node => node.nodeType === Node.ELEMENT_NODE && /^(THEAD|TBODY|TFOOT|TR)$/.test(node.tagName);
|
|
1343
1585
|
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()) {
|
|
1586
|
+
for (let child = parent.firstChild; child; child = child.nextSibling) {
|
|
1347
1587
|
yield;
|
|
1348
1588
|
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)) {
|
|
1589
|
+
// A clipped table can cross an output fragment. Retain its words,
|
|
1590
|
+
// not detached rows, until the next non-table node closes the block.
|
|
1591
|
+
if (!current.table) { flush(current); current.table = []; }
|
|
1592
|
+
current.table.push(yield* _rapierPortableTableTextSteps(child, opts));
|
|
1358
1593
|
continue;
|
|
1359
|
-
}
|
|
1360
|
-
|
|
1594
|
+
}
|
|
1595
|
+
if (current.table && blankText(child)) continue;
|
|
1596
|
+
if (current.table) flush(current);
|
|
1597
|
+
if (child.nodeType === Node.ELEMENT_NODE && blockTags.has(child.tagName)) {
|
|
1598
|
+
flush(current);
|
|
1361
1599
|
yield* collectBlock(child);
|
|
1362
1600
|
} else if (opts.visible && child.nodeType === Node.ELEMENT_NODE && child.tagName === 'CODE' && String(child.textContent || '').includes('\n')) {
|
|
1363
|
-
// Lines
|
|
1364
|
-
flush();
|
|
1601
|
+
// Lines clipped from a code block retain every authored space.
|
|
1602
|
+
flush(current);
|
|
1365
1603
|
pushBlock(String(child.textContent || ''), true);
|
|
1366
|
-
} else
|
|
1367
|
-
inline.push(child);
|
|
1368
|
-
}
|
|
1604
|
+
} else current.inline.push(yield* _rapierPortableInlineTextSteps(child, opts));
|
|
1369
1605
|
}
|
|
1370
|
-
flush();
|
|
1606
|
+
if (complete) flush(current);
|
|
1371
1607
|
};
|
|
1372
1608
|
|
|
1373
1609
|
const collectBlock = function* (node) {
|
|
1374
1610
|
if (!node || node.nodeType !== Node.ELEMENT_NODE) return;
|
|
1375
1611
|
const tag = node.tagName;
|
|
1376
1612
|
if (/^H[1-6]$/.test(tag) || tag === 'P') {
|
|
1377
|
-
const text = _rapierNormalizeInlineText(
|
|
1613
|
+
const text = _rapierNormalizeInlineText(yield* _rapierPortableInlineTextSteps(node, opts));
|
|
1378
1614
|
// 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
1615
|
if (tag === 'P' && !text && node.childNodes.length && !node.hasAttribute('data-md-break')) blocks.push({ text: '', blank: true });
|
|
1380
1616
|
else pushBlock(text, false);
|
|
@@ -1391,11 +1627,11 @@ function* _rapierPortablePlainTextSteps(root, options) {
|
|
|
1391
1627
|
return;
|
|
1392
1628
|
}
|
|
1393
1629
|
if (tag === 'UL' || tag === 'OL') {
|
|
1394
|
-
pushBlock(
|
|
1630
|
+
pushBlock(yield* _rapierPortableListTextSteps(node, 0, opts), false);
|
|
1395
1631
|
return;
|
|
1396
1632
|
}
|
|
1397
1633
|
if (tag === 'TABLE') {
|
|
1398
|
-
pushBlock(
|
|
1634
|
+
pushBlock(yield* _rapierPortableTableTextSteps(node, opts), false);
|
|
1399
1635
|
return;
|
|
1400
1636
|
}
|
|
1401
1637
|
if (tag === 'BLOCKQUOTE') {
|
|
@@ -1409,10 +1645,10 @@ function* _rapierPortablePlainTextSteps(root, options) {
|
|
|
1409
1645
|
const children = Array.from(node.children);
|
|
1410
1646
|
for (let i = 0; i < children.length; i++) {
|
|
1411
1647
|
if (children[i].tagName !== 'DT') continue;
|
|
1412
|
-
const term = _rapierNormalizeInlineText(
|
|
1648
|
+
const term = _rapierNormalizeInlineText(yield* _rapierPortableInlineTextSteps(children[i], opts));
|
|
1413
1649
|
const defs = [];
|
|
1414
1650
|
while (children[i + 1] && children[i + 1].tagName === 'DD') {
|
|
1415
|
-
defs.push(_rapierNormalizeInlineText(
|
|
1651
|
+
defs.push(_rapierNormalizeInlineText(yield* _rapierPortableInlineTextSteps(children[++i], opts)));
|
|
1416
1652
|
}
|
|
1417
1653
|
pushBlock(term + (defs.length ? ' — ' + defs.join('\n ') : ''), false);
|
|
1418
1654
|
}
|
|
@@ -1420,14 +1656,16 @@ function* _rapierPortablePlainTextSteps(root, options) {
|
|
|
1420
1656
|
}
|
|
1421
1657
|
if (tag === 'DETAILS') {
|
|
1422
1658
|
const summary = node.querySelector(':scope > summary');
|
|
1423
|
-
if (summary) pushBlock(_rapierNormalizeInlineText(
|
|
1659
|
+
if (summary) pushBlock(_rapierNormalizeInlineText(yield* _rapierPortableInlineTextSteps(summary, opts)), false);
|
|
1424
1660
|
for (const child of node.children) if (child !== summary) { yield; yield* collectBlock(child); }
|
|
1425
1661
|
return;
|
|
1426
1662
|
}
|
|
1427
1663
|
yield* collectChildren(node);
|
|
1428
1664
|
};
|
|
1429
1665
|
|
|
1430
|
-
|
|
1666
|
+
const pending = state();
|
|
1667
|
+
return {write: root => collectChildren(root, pending, false), finish: function* () {
|
|
1668
|
+
flush(pending);
|
|
1431
1669
|
// Each block stands a blank line from the next, and each empty paragraph between them adds one newline more.
|
|
1432
1670
|
let joined = '', blanks = 0;
|
|
1433
1671
|
for (const block of blocks) {
|
|
@@ -1438,6 +1676,7 @@ function* _rapierPortablePlainTextSteps(root, options) {
|
|
|
1438
1676
|
}
|
|
1439
1677
|
const text = joined.trim();
|
|
1440
1678
|
return opts.finalNewline && text ? text + '\n' : text;
|
|
1679
|
+
}};
|
|
1441
1680
|
}
|
|
1442
1681
|
|
|
1443
1682
|
// The text file Export writes: a Markdown document's portable words and one final newline, any other document's own text.
|
|
@@ -1490,8 +1729,21 @@ function _rapierBidiStrong(cp) {
|
|
|
1490
1729
|
return null;
|
|
1491
1730
|
}
|
|
1492
1731
|
|
|
1493
|
-
|
|
1732
|
+
// A consuming operation, not an editable DOM mode. Both outputs share one admitted
|
|
1733
|
+
// parse per equation; callers decide whether and how to expose the MathML to people.
|
|
1734
|
+
function renderWithMathML(source, metadata = {}) {
|
|
1735
|
+
const provider = _rapierProviders.math;
|
|
1736
|
+
if (!provider || provider.status !== 'ready' || typeof provider.renderToString !== 'function' ||
|
|
1737
|
+
typeof provider.renderWithMathML !== 'function')
|
|
1738
|
+
throw new Error('The Rapier Math shared-result renderer is unavailable.');
|
|
1739
|
+
if (!md) initMarkdownIt();
|
|
1740
|
+
const equations = [];
|
|
1741
|
+
const html = _rapierRenderSemanticRoot(source, metadata, {mathEquations: equations}).innerHTML;
|
|
1742
|
+
return {html, equations};
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
return {renderWithMathML, _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, _rapierApplyImageGeometry, _rapierImageAltSize, _rapierImageAltSourceParts, _rapierImageAltText, _rapierImageSizeAttributes, _rapierRawHtmlFragment, _rapierApplyBlockDirection, _rapierFirstStrongDir, _rapierAssignHeadingSlugs, _rapierDisambiguateRenderedAnchors, _rapierDecodeHash, _rapierDecorateJumpLists, _rapierMarkFigureCaptions, _rapierMarkListContinuations, _rapierMarkTableCaptions, _rapierNormalizeCodeElement, _rapierLanguageClass, _relabelFootnoteHtml, _footnoteIdFromLabel, wrapDisplayMath, parser: () => md};
|
|
1494
1746
|
}
|
|
1495
1747
|
export {RAPIER_TABLE_CAPTION_PREFIX_RE as tableCaptionPrefix, RAPIER_PORTABLE_HIGHLIGHT_HEX as portableHighlightHex, _RAPIER_RTL_SCRIPT as rtlScripts, createMarkdownRenderer};
|
|
1496
1748
|
|
|
1497
|
-
export {mountHtmlBlocks, htmlImageRecord, htmlLayoutEdit};
|
|
1749
|
+
export {mountHtmlBlocks, materializeObjects, objectPosterLabel, htmlImageRecord, htmlLayoutEdit, htmlModeEdit, mergeHtmlObjectBlocks};
|