rapier-markdown-kit 1.2.33 → 1.2.42
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/README.md +15 -9
- package/dist/agent/vendor/pretext/NOTICE +2 -2
- package/dist/agent/vendor/pretext/SOURCE.json +62 -78
- package/dist/agent/vendor/pretext/analysis.js +24 -6
- package/dist/agent/vendor/pretext/gecko-line-breaks.js +1 -1
- package/dist/agent/vendor/pretext/generated/engine-break-data.js +1 -1
- package/dist/agent/vendor/pretext/generated/webkit-generic-families.js +1 -1
- package/dist/agent/vendor/pretext/graphemes.js +1 -1
- package/dist/agent/vendor/pretext/han-kerning.js +1 -1
- package/dist/agent/vendor/pretext/layout.js +14 -11
- package/dist/agent/vendor/pretext/line-break.js +170 -195
- package/dist/agent/vendor/pretext/line-breaks.js +1 -7
- package/dist/agent/vendor/pretext/line-text.js +2 -2
- package/dist/agent/vendor/pretext/measurement.js +454 -113
- package/dist/agent/vendor/pretext/prepare.js +164 -244
- package/dist/agent/vendor/pretext/rich-inline.js +57 -37
- package/dist/agent/will.mjs +73 -0
- package/dist/kit/ledger/data.mjs +57 -4
- package/dist/kit/ledger/format.mjs +114 -47
- package/dist/kit/ledger/hash.mjs +43 -2
- package/dist/kit/ledger/history.mjs +6 -0
- package/dist/kit/ledger/inverse-source.mjs +15 -2
- package/dist/kit/ledger/journal-records.mjs +32 -9
- package/dist/kit/ledger/merge.mjs +3 -17
- package/dist/kit/ledger/text-merge.mjs +28 -2
- package/dist/kit/rapier-ledger.mjs +2 -2
- package/dist/kit/render-markdown.mjs +170 -12
- package/dist/kit/render-print.mjs +3 -3
- package/dist/kit/render-sanitize.mjs +66 -23
- package/dist/kit/render.mjs +10 -10
- package/dist/kit/wiki.mjs +34 -0
- package/dist/kit/will.d.mts +3 -0
- package/dist/layout/hyphenation.mjs +6 -4
- package/dist/layout/line-plan.mjs +96 -0
- package/dist/layout/model.mjs +2 -1
- package/dist/layout/paragraph-fit.mjs +4 -4
- package/dist/layout/prepared-paragraph.mjs +19 -2
- package/dist/layout/typography.mjs +57 -63
- package/dist/spec/document-fonts.mjs +1 -0
- package/dist/spec/document-settings.mjs +1 -0
- package/dist/spec/html-blocks.mjs +10 -7
- package/dist/spec/md-assets.mjs +52 -12
- package/package.json +1 -1
- package/style.css +33 -23
- package/dist/agent/vendor/pretext/entry-geometry.js +0 -183
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
import {htmlPlaceholder, mountHtmlBlocks, materializeObjects, objectPosterLabel, htmlImageRecord, htmlLayoutEdit, htmlModeEdit, mergeHtmlObjectBlocks} from '../spec/html-blocks.mjs';
|
|
3
3
|
import {finish, finishAsync, cloneTree, cleanTree, serializeTree, htmlParts, admitHtml, htmlBoundary, pause, check} from './render-work.mjs';
|
|
4
|
+
import {installWikiParser, wikiAddress} from './wiki.mjs';
|
|
4
5
|
const RAPIER_TABLE_CAPTION_PREFIX_RE = /^(?:Table:|:) /;
|
|
5
6
|
const RAPIER_PORTABLE_HIGHLIGHT_HEX = Object.freeze({
|
|
6
7
|
green: '#35c57a', red: '#f05a62', blue: '#4f91f7', yellow: '#e6b91e', purple: '#9b6dea',
|
|
@@ -9,6 +10,51 @@ const _RAPIER_RTL_SCRIPT = /[\p{Script=Hebrew}\p{Script=Arabic}\p{Script=Syriac}
|
|
|
9
10
|
function createMarkdownRenderer(runtime) {
|
|
10
11
|
const {Node, _rapierPrefixPortableAnchors, RAPIER_HIGHLIGHT_COLOR_BY_MARKER, RAPIER_MARKDOWN_SPEC, RAPIER_RENDERED_HEADING_SELECTOR, _rapierApplyMarkdownSpec, _rapierChromeOwnsId, _rapierCodeHtml, _rapierDeriveDarkColor, _rapierDormantHeadings, _rapierEmbedAssetSource, _rapierHeadingSlugBase, _rapierNextHeadingSlug, _rapierHighlightAdmitted, _rapierInstallMarkdownMath, _rapierMarkdownPreview, _rapierPlainLayout, _rapierProviders, _rapierRemoteContent, _rapierRemoteImagePlaceholder, _rapierRemoteSubresourceOrigin, _rapierRenderedAnchorOriginalIds, _rapierSourceCharEscaped, _rapierSplitOpeningFrontmatter, _rapierUiDiagram, _rapierUiMath, document, escapeRapierHtmlText, globalThis, sanitizeRapierHtml, window} = runtime;
|
|
11
12
|
let md = runtime.md;
|
|
13
|
+
let wikiTimer = null, wikiAttempt = 0;
|
|
14
|
+
const wikiBusy = new WeakSet();
|
|
15
|
+
function scheduleWikiEmbeds() {
|
|
16
|
+
if (!globalThis.RapierPlatform || !document || wikiTimer !== null) return;
|
|
17
|
+
wikiTimer = setTimeout(async () => {
|
|
18
|
+
wikiTimer = null;
|
|
19
|
+
const original = globalThis.RapierOriginalFolderCore?.currentOriginal();
|
|
20
|
+
if (!original) { if (++wikiAttempt < 8) scheduleWikiEmbeds(); return; }
|
|
21
|
+
wikiAttempt = 0;
|
|
22
|
+
for (const node of [...document.querySelectorAll('#editor-blocks .rapier-source-token--wiki-embed')].slice(0, 32)) {
|
|
23
|
+
if (wikiBusy.has(node) || node.hasAttribute('data-rapier-wiki-ready')) continue;
|
|
24
|
+
wikiBusy.add(node);
|
|
25
|
+
try {
|
|
26
|
+
const link = node.querySelector('a'), href = link?.getAttribute('href');
|
|
27
|
+
if (!href) continue;
|
|
28
|
+
const before = node.innerHTML;
|
|
29
|
+
const embedded = await original.embed(href);
|
|
30
|
+
if (!node.isConnected || node.innerHTML !== before || globalThis.RapierOriginalFolderCore?.currentOriginal() !== original) continue;
|
|
31
|
+
const holder = _rapierRenderSemanticRoot(embedded.text, {docKind: 'markdown'}, {wikiEmbedding: true});
|
|
32
|
+
let followed = 0;
|
|
33
|
+
for (const anchor of holder.querySelectorAll('a[href]')) {
|
|
34
|
+
const address = anchor.getAttribute('href');
|
|
35
|
+
if (/^(?:[a-z][a-z0-9+.-]*:|\/)/i.test(address)) continue;
|
|
36
|
+
try {
|
|
37
|
+
if (++followed > 64) throw new Error('link limit');
|
|
38
|
+
anchor.setAttribute('href', await original.addressFrom(embedded.displayPath, address));
|
|
39
|
+
} catch (_) { anchor.replaceWith(...anchor.childNodes); }
|
|
40
|
+
}
|
|
41
|
+
if (!node.isConnected || node.innerHTML !== before || globalThis.RapierOriginalFolderCore?.currentOriginal() !== original) continue;
|
|
42
|
+
for (const image of holder.querySelectorAll('img,[data-rapier-remote-src]'))
|
|
43
|
+
image.replaceWith(document.createTextNode(image.getAttribute('alt') || image.textContent || ''));
|
|
44
|
+
for (const block of [...holder.querySelectorAll('p,h1,h2,h3,h4,h5,h6,div,pre,blockquote,ul,ol,li,table,tr,td,th')].reverse())
|
|
45
|
+
block.replaceWith(...block.childNodes, document.createElement('br'));
|
|
46
|
+
node.replaceChildren(...holder.childNodes);
|
|
47
|
+
node.setAttribute('data-rapier-visible', encodeURIComponent(node.textContent));
|
|
48
|
+
node.setAttribute('data-rapier-wiki-ready', '');
|
|
49
|
+
node.contentEditable = 'false';
|
|
50
|
+
} catch (_) { }
|
|
51
|
+
finally { wikiBusy.delete(node); }
|
|
52
|
+
}
|
|
53
|
+
}, 125);
|
|
54
|
+
}
|
|
55
|
+
window?.addEventListener?.('rapier:original-file-ready', scheduleWikiEmbeds);
|
|
56
|
+
const wikiMode = options => options.wikiEmbedding ? 'links' : options.wikiLinks !== undefined ? options.wikiLinks
|
|
57
|
+
: globalThis.RapierOriginalFolderCore?.currentOriginal?.() ? 'label' : false;
|
|
12
58
|
function initMarkdownIt() {
|
|
13
59
|
if (typeof window.markdownit !== 'function') throw new Error('markdownit');
|
|
14
60
|
if (typeof _rapierMarkdownPreview === 'function') globalThis.rapierRenderPreview = _rapierMarkdownPreview;
|
|
@@ -82,6 +128,21 @@ function initMarkdownIt() {
|
|
|
82
128
|
const sourceToken = (source, visible, className) =>
|
|
83
129
|
'<span class="rapier-source-token ' + className + '" data-rapier-source="' +
|
|
84
130
|
encodeURIComponent(source) + '" data-rapier-visible="' + encodeURIComponent(visible) + '">' + md.utils.escapeHtml(visible) + '</span>';
|
|
131
|
+
installWikiParser(md, (token, env) => {
|
|
132
|
+
const {target, label, embed} = token.meta, href = wikiAddress(target), esc = md.utils.escapeHtml;
|
|
133
|
+
if (env.rapierWikiLinks === 'label') return esc(label);
|
|
134
|
+
if (embed && /\.(?:png|jpe?g|gif|webp|jxl)(?:#.*)?$/i.test(target)) {
|
|
135
|
+
if (!env.rapierWikiEmbedding && globalThis.RapierPlatform?.files.relativeCandidate?.(href) && globalThis.RapierEmbeddedImages?.imageHtml)
|
|
136
|
+
return globalThis.RapierEmbeddedImages.imageHtml(null, label, '', null, '', label, token.content, href, env.rapierImagePresentation !== false);
|
|
137
|
+
return '<span data-rapier-remote-src="' + esc(href) + '" data-rapier-remote-alt="' + esc(label) +
|
|
138
|
+
'" data-rapier-image-source="' + esc(encodeURIComponent(token.content)) + '">' + esc(label) + '</span>';
|
|
139
|
+
}
|
|
140
|
+
const note = embed && !env.rapierWikiEmbedding;
|
|
141
|
+
if (note) scheduleWikiEmbeds();
|
|
142
|
+
return '<span class="rapier-source-token rapier-source-token--wiki' + (note ? '-embed' : '') +
|
|
143
|
+
'" data-rapier-source="' + esc(encodeURIComponent(token.content)) + '" data-rapier-visible="' + esc(encodeURIComponent(label)) +
|
|
144
|
+
'"><a href="' + esc(href) + '">' + esc(label) + '</a></span>';
|
|
145
|
+
});
|
|
85
146
|
md.renderer.rules.emoji = (tokens, idx) => {
|
|
86
147
|
const token = tokens[idx];
|
|
87
148
|
const source = token.markup ? ':' + token.markup + ':' : token.content;
|
|
@@ -114,7 +175,9 @@ function initMarkdownIt() {
|
|
|
114
175
|
|
|
115
176
|
md.renderer.rules.image = (tokens, idx, opts, env, self) => {
|
|
116
177
|
const token = tokens[idx];
|
|
117
|
-
const
|
|
178
|
+
const written = token.attrGet('src') || '';
|
|
179
|
+
const hosted = globalThis.RapierEmbedPictureBase?.(written) ?? null;
|
|
180
|
+
const src = hosted || written;
|
|
118
181
|
const title = token.attrGet('title') || '';
|
|
119
182
|
const rawAlt = String(token.content ?? token.attrGet('alt') ?? '');
|
|
120
183
|
const size = _rapierImageAltSize(rawAlt);
|
|
@@ -140,7 +203,7 @@ function initMarkdownIt() {
|
|
|
140
203
|
+ (token.meta?.mdImage?.source ? ' data-rapier-image-source="' + escape(encodeURIComponent(token.meta.mdImage.source)) + '"' : '')
|
|
141
204
|
+ '>' + escape(alt || src) + '</span>';
|
|
142
205
|
}
|
|
143
|
-
if (!env.rapierPage && !_rapierRemoteContent.allowed && _rapierRemoteSubresourceOrigin(src)) {
|
|
206
|
+
if (!hosted && !env.rapierPage && !_rapierRemoteContent.allowed && _rapierRemoteSubresourceOrigin(src)) {
|
|
144
207
|
const html = _rapierRemoteImagePlaceholder(src, alt, title, size, layout, rawAlt);
|
|
145
208
|
return html.replace('<span ', '<span data-rapier-markdown-image="" ' +
|
|
146
209
|
(token.meta?.mdImage?.source ? 'data-rapier-image-source="' + md.utils.escapeHtml(encodeURIComponent(token.meta.mdImage.source)) + '" ' : ''));
|
|
@@ -277,6 +340,7 @@ function initMarkdownIt() {
|
|
|
277
340
|
};
|
|
278
341
|
|
|
279
342
|
const _fenceDefault = md.renderer.rules.fence;
|
|
343
|
+
let _mathPromptedOnce = false;
|
|
280
344
|
{
|
|
281
345
|
let _diagramPromptedOnce = false;
|
|
282
346
|
md.renderer.rules.fence = function (tokens, idx, opts, env, self) {
|
|
@@ -303,8 +367,16 @@ function initMarkdownIt() {
|
|
|
303
367
|
}
|
|
304
368
|
const provider = env.rapierProviders === false ? null : _rapierProviders.mermaid;
|
|
305
369
|
const native = globalThis.RapierFlowchart?.parseFlowchart(src).ok === true;
|
|
306
|
-
const
|
|
307
|
-
|
|
370
|
+
const diagramReady = native || !!(provider && provider.status === 'ready' && typeof provider.renderToString === 'function');
|
|
371
|
+
const mathProvider = env.rapierProviders === false ? null : _rapierProviders.math;
|
|
372
|
+
const mathNeeded = provider?.requiresMath?.(src) === true;
|
|
373
|
+
const mathReady = !mathNeeded || mathProvider?.status === 'ready';
|
|
374
|
+
if (!mathReady && !mathProvider?.request?.() && env.rapierProviders !== false && !_mathPromptedOnce) {
|
|
375
|
+
_mathPromptedOnce = true;
|
|
376
|
+
try { _rapierUiMath.request(); } catch (_) {}
|
|
377
|
+
}
|
|
378
|
+
const ready = diagramReady && mathReady;
|
|
379
|
+
if (!diagramReady && !provider?.request?.() && env.rapierProviders !== false && !_diagramPromptedOnce) {
|
|
308
380
|
_diagramPromptedOnce = true;
|
|
309
381
|
try { _rapierUiDiagram.request(); } catch (_) {}
|
|
310
382
|
}
|
|
@@ -338,7 +410,6 @@ function initMarkdownIt() {
|
|
|
338
410
|
preserveEmphasisMarkup('strong_open', 'data-rapier-strong');
|
|
339
411
|
|
|
340
412
|
{
|
|
341
|
-
let _mathPromptedOnce = false;
|
|
342
413
|
const _renderMath = (src, displayMode, env, authored) => {
|
|
343
414
|
const marker = displayMode ? '$$' : '$';
|
|
344
415
|
const source = authored || marker + src + marker, literal = md.utils.escapeHtml(source);
|
|
@@ -381,6 +452,10 @@ function initMarkdownIt() {
|
|
|
381
452
|
}
|
|
382
453
|
globalThis.RapierMarkdownLayout.installMarkdownLayout(md);
|
|
383
454
|
globalThis.RapierImageAssets.configureParser(md, _rapierSplitOpeningFrontmatter);
|
|
455
|
+
const render = md.renderer.render;
|
|
456
|
+
md.renderer.render = function (tokens, options, env) {
|
|
457
|
+
return render.call(this, _rapierCloseOpenDetails(tokens), options, env);
|
|
458
|
+
};
|
|
384
459
|
}
|
|
385
460
|
|
|
386
461
|
function _rapierRenderSemanticRoot(canonical, metadata, options = {}) {
|
|
@@ -402,8 +477,21 @@ function _rapierRenderSemanticRoot(canonical, metadata, options = {}) {
|
|
|
402
477
|
|
|
403
478
|
const markdown = _rapierSplitOpeningFrontmatter(canonical).body;
|
|
404
479
|
const env = {rapierImagePresentation: options.imagePresentation === true, ...(options.page ? {rapierPage: true} : {}), ...(options.providers === false ? {rapierProviders: false} : {})};
|
|
480
|
+
if (options.wikiEmbedding) env.rapierWikiEmbedding = true;
|
|
481
|
+
const wikiLinks = wikiMode(options);
|
|
482
|
+
if (wikiLinks) env.rapierWikiLinks = wikiLinks;
|
|
405
483
|
if (options.mathEquations) env.rapierMathEquations = options.mathEquations;
|
|
406
|
-
|
|
484
|
+
const nesting = md && options.maxNesting > 0 ? options.maxNesting : 0;
|
|
485
|
+
let cutAt = -1, rendered;
|
|
486
|
+
if (nesting || md && options.closeDetails) {
|
|
487
|
+
const saved = md.options.maxNesting;
|
|
488
|
+
if (nesting) md.options.maxNesting = nesting;
|
|
489
|
+
try {
|
|
490
|
+
const tokens = md.parse(markdown, env);
|
|
491
|
+
if (nesting) cutAt = _rapierNestingCutAt(tokens, nesting);
|
|
492
|
+
rendered = md.renderer.render(options.closeDetails ? _rapierCloseOpenDetails(tokens) : tokens, md.options, env);
|
|
493
|
+
} finally { md.options.maxNesting = saved; }
|
|
494
|
+
} else rendered = md
|
|
407
495
|
? md.render(markdown, env)
|
|
408
496
|
: '<pre><code>' + escapeRapierHtmlText(markdown) + '</code></pre>';
|
|
409
497
|
if (md && env.footnotes && Array.isArray(env.footnotes.list) && env.footnotes.list.length) {
|
|
@@ -412,6 +500,7 @@ function _rapierRenderSemanticRoot(canonical, metadata, options = {}) {
|
|
|
412
500
|
rendered = _relabelFootnoteHtml(rendered, labels);
|
|
413
501
|
}
|
|
414
502
|
root.innerHTML = sanitizeRapierHtml(rendered, options.page ? 'source' : 'export');
|
|
503
|
+
if (cutAt >= 0) _rapierAppendNestingCut(root, markdown, cutAt);
|
|
415
504
|
_rapierApplyBlockDirection(root);
|
|
416
505
|
|
|
417
506
|
_rapierAssignHeadingSlugs(root, RAPIER_RENDERED_HEADING_SELECTOR, false);
|
|
@@ -425,6 +514,15 @@ function _rapierRenderSemanticRoot(canonical, metadata, options = {}) {
|
|
|
425
514
|
return root;
|
|
426
515
|
}
|
|
427
516
|
|
|
517
|
+
function _rapierAppendNestingCut(root, markdown, cutAt) {
|
|
518
|
+
const note = root.ownerDocument.createElement('p'), pre = root.ownerDocument.createElement('pre'), code = root.ownerDocument.createElement('code');
|
|
519
|
+
note.setAttribute('data-rapier-nesting-cut', 'true');
|
|
520
|
+
note.textContent = 'Nesting is too deep to show here. The rest of the document follows as plain text.';
|
|
521
|
+
code.textContent = String(markdown).split('\n').slice(cutAt).join('\n');
|
|
522
|
+
pre.append(code);
|
|
523
|
+
root.append(note, pre);
|
|
524
|
+
}
|
|
525
|
+
const _rapierNestingCutAt = (tokens, nesting) => { const deepest = tokens.find(token => token.level >= nesting - 1 && token.map); return deepest ? deepest.map[0] + 1 : -1; };
|
|
428
526
|
async function _rapierRenderSemanticRootAsync(canonical, metadata, options = {}) {
|
|
429
527
|
const work = options.work || options;
|
|
430
528
|
check(work);
|
|
@@ -435,8 +533,16 @@ async function _rapierRenderSemanticRootAsync(canonical, metadata, options = {})
|
|
|
435
533
|
}
|
|
436
534
|
const markdown = _rapierSplitOpeningFrontmatter(canonical).body;
|
|
437
535
|
const fallbackEnv = options.page ? {rapierPage: true} : {};
|
|
438
|
-
|
|
536
|
+
const wikiLinks = markdown.includes('[[') && wikiMode(options);
|
|
537
|
+
if (wikiLinks) fallbackEnv.rapierWikiLinks = wikiLinks;
|
|
538
|
+
const nesting = options.maxNesting > 0 ? options.maxNesting : 0, saved = md.options.maxNesting;
|
|
539
|
+
if (nesting) md.options.maxNesting = nesting;
|
|
540
|
+
let parsed;
|
|
541
|
+
try { parsed = !wikiLinks && options.parsed || (runtime.parseAsync && !wikiLinks && !nesting ? await runtime.parseAsync(markdown, options) : {tokens: md.parse(markdown, fallbackEnv), env: fallbackEnv}); }
|
|
542
|
+
finally { md.options.maxNesting = saved; }
|
|
439
543
|
let {tokens, env} = parsed, labels = env.footnotes?.list?.map((item, index) => String(item?.label || ('inline-' + (index + 1))));
|
|
544
|
+
const cutAt = nesting ? _rapierNestingCutAt(tokens, nesting) : -1;
|
|
545
|
+
tokens = _rapierCloseOpenDetails(tokens);
|
|
440
546
|
env = {...env, rapierImagePresentation: options.imagePresentation === true};
|
|
441
547
|
await pause(work, .3);
|
|
442
548
|
const inert = document.createElement('template').content.ownerDocument;
|
|
@@ -463,6 +569,7 @@ async function _rapierRenderSemanticRootAsync(canonical, metadata, options = {})
|
|
|
463
569
|
}
|
|
464
570
|
}
|
|
465
571
|
if (html) append();
|
|
572
|
+
if (cutAt >= 0) _rapierAppendNestingCut(root, markdown, cutAt);
|
|
466
573
|
parsed = tokens = env = labels = null;
|
|
467
574
|
await finishAsync(_rapierSemanticDecorations(root), work);
|
|
468
575
|
await pause(work, .6);
|
|
@@ -727,6 +834,49 @@ function _rapierImageSizeAttributes(size, marker, geometry, automatic = true) {
|
|
|
727
834
|
return attributes + (style ? ' style="' + style + '"' : '');
|
|
728
835
|
}
|
|
729
836
|
|
|
837
|
+
function _rapierCloseOpenDetails(tokens) {
|
|
838
|
+
const opens = /^<details[\t\n\f\r />]/i, closes = /^<\/details[\t\n\f\r ]*>$/i;
|
|
839
|
+
const tags = token => {
|
|
840
|
+
const found = [];
|
|
841
|
+
if (token.type === 'html_inline') found.push(token.content);
|
|
842
|
+
else if (token.type === 'html_block') {
|
|
843
|
+
let raw = '';
|
|
844
|
+
_rapierRawHtmlFragment(token.content, tag => {
|
|
845
|
+
if (raw) { if (new RegExp('^</' + raw + '[\\t\\n\\f\\r ]*>$', 'i').test(tag)) raw = ''; }
|
|
846
|
+
else {
|
|
847
|
+
const literal = /^<(script|style|textarea|title|xmp|iframe|noembed|noframes|plaintext)(?=[\t\n\f\r />])/i.exec(tag);
|
|
848
|
+
if (literal) raw = literal[1];
|
|
849
|
+
else found.push(tag);
|
|
850
|
+
}
|
|
851
|
+
return tag;
|
|
852
|
+
}, md);
|
|
853
|
+
}
|
|
854
|
+
else if (token.type === 'inline') for (const child of token.children || []) if (child.type === 'html_inline') found.push(child.content);
|
|
855
|
+
return found;
|
|
856
|
+
};
|
|
857
|
+
const after = new Map(), add = (index, count) => after.set(index, (after.get(index) || 0) + count);
|
|
858
|
+
const waiting = [];
|
|
859
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
860
|
+
const token = tokens[index];
|
|
861
|
+
let depth = 0;
|
|
862
|
+
for (const tag of tags(token)) {
|
|
863
|
+
if (opens.test(tag)) { if (token.type === 'html_block') waiting.push(index); else depth++; }
|
|
864
|
+
else if (closes.test(tag)) { if (depth) depth--; else waiting.pop(); }
|
|
865
|
+
}
|
|
866
|
+
if (depth) add(index + 1 < tokens.length && tokens[index + 1].nesting === -1 ? index + 1 : index, depth);
|
|
867
|
+
}
|
|
868
|
+
for (const index of waiting) add(index, 1);
|
|
869
|
+
if (!after.size) return tokens;
|
|
870
|
+
const out = [];
|
|
871
|
+
tokens.forEach((token, index) => {
|
|
872
|
+
out.push(token);
|
|
873
|
+
const count = after.get(index);
|
|
874
|
+
if (count) out.push({type: 'html_block', tag: '', attrs: null, map: null, nesting: 0, level: token.level || 0, children: null,
|
|
875
|
+
content: '</details>'.repeat(count) + '\n', markup: '', info: '', meta: null, block: true, hidden: false});
|
|
876
|
+
});
|
|
877
|
+
return out;
|
|
878
|
+
}
|
|
879
|
+
|
|
730
880
|
function _rapierRawHtmlFragment(source, rewrite, parser) {
|
|
731
881
|
const state = new parser.inline.State(source, parser, {}, []);
|
|
732
882
|
const htmlRule = parser.inline.ruler.__rules__.find(rule => rule.name === 'html_inline').fn;
|
|
@@ -792,7 +942,9 @@ function* _rapierAssignHeadingSlugsSteps(root, selector = RAPIER_RENDERED_HEADIN
|
|
|
792
942
|
if (!headingSet.has(element) && element.id) used[element.id] = 0;
|
|
793
943
|
}
|
|
794
944
|
for (const heading of headings) { yield;
|
|
795
|
-
|
|
945
|
+
const base = _rapierHeadingSlugBase(heading.textContent);
|
|
946
|
+
const owned = typeof _rapierChromeOwnsId === 'function' && _rapierChromeOwnsId(base);
|
|
947
|
+
heading.id = _rapierNextHeadingSlug(owned ? 'heading-' + base : base, used);
|
|
796
948
|
}
|
|
797
949
|
}
|
|
798
950
|
|
|
@@ -1622,11 +1774,17 @@ async function _rapierPlainTextFileAsync(context) {
|
|
|
1622
1774
|
async function _rapierCloneRootAsync(root, work) { return finishAsync(cloneTree(root), work); }
|
|
1623
1775
|
|
|
1624
1776
|
function _rapierSourceTokenValue(node) {
|
|
1625
|
-
if (!node?.hasAttribute('data-rapier-source') || !node.hasAttribute('data-rapier-visible')
|
|
1626
|
-
Array.from(node.childNodes).some(child => child.nodeType !== Node.TEXT_NODE)) return null;
|
|
1777
|
+
if (!node?.hasAttribute('data-rapier-source') || !node.hasAttribute('data-rapier-visible')) return null;
|
|
1627
1778
|
try {
|
|
1779
|
+
const source = decodeURIComponent(node.getAttribute('data-rapier-source'));
|
|
1780
|
+
if (node.classList.contains('rapier-source-token--wiki') || node.classList.contains('rapier-source-token--wiki-embed')) {
|
|
1781
|
+
const token = md.parseInline(source, {rapierWikiLinks: 'links'})[0]?.children;
|
|
1782
|
+
if (token?.length !== 1 || token[0].type !== 'rapier_wiki') return null;
|
|
1783
|
+
if (!node.hasAttribute('data-rapier-wiki-ready') && (node.children.length !== 1 || node.firstElementChild.tagName !== 'A' ||
|
|
1784
|
+
node.firstElementChild.getAttribute('href') !== wikiAddress(token[0].meta.target))) return null;
|
|
1785
|
+
} else if (Array.from(node.childNodes).some(child => child.nodeType !== Node.TEXT_NODE)) return null;
|
|
1628
1786
|
return node.textContent === decodeURIComponent(node.getAttribute('data-rapier-visible'))
|
|
1629
|
-
?
|
|
1787
|
+
? source : null;
|
|
1630
1788
|
} catch (_) { return null; }
|
|
1631
1789
|
}
|
|
1632
1790
|
|
|
@@ -1634,7 +1792,7 @@ function _rapierResolveSoftBreakTokens(root) { return finish(_rapierResolveSoftB
|
|
|
1634
1792
|
|
|
1635
1793
|
function* _rapierResolveSoftBreakTokensSteps(root) {
|
|
1636
1794
|
for (const token of root.querySelectorAll('.rapier-source-token--softbreak')) { yield;
|
|
1637
|
-
if (_rapierSourceTokenValue(token) === '\n') token.replaceWith(document.createTextNode(' '));
|
|
1795
|
+
if (_rapierSourceTokenValue(token) === '\n') token.replaceWith(document.createTextNode(token.closest('pre') ? '\n' : ' '));
|
|
1638
1796
|
else token.replaceWith(...token.childNodes);
|
|
1639
1797
|
}
|
|
1640
1798
|
return root;
|
|
@@ -3,7 +3,7 @@ import {collectPdfVectors} from './pdf-vectors.mjs';
|
|
|
3
3
|
import {collectPdfGlyphs} from './pdf-glyphs.mjs';
|
|
4
4
|
import {pdfFontSources, waitForFonts} from './export-fonts.mjs';
|
|
5
5
|
import {apply as applyTypography} from '../layout/typography.mjs';
|
|
6
|
-
import {documentLanguage, pageSizeOf} from '../spec/document-settings.mjs';
|
|
6
|
+
import {documentLanguage, pageSizeOf, paperForLanguage} from '../spec/document-settings.mjs';
|
|
7
7
|
import {applyPrintCapitals} from './print-capitals.mjs';
|
|
8
8
|
import {stripOneTerminator as _rapierWillStripOneTerminator} from '../agent/will.mjs';
|
|
9
9
|
import {quotedWillSpan} from '../spec/md-marks.mjs';
|
|
@@ -220,7 +220,7 @@ async function _rapierWritePrintPdf({root, css, fontCss = '', canonical, bom = f
|
|
|
220
220
|
if (current?.() === false) refuse('document_changed');
|
|
221
221
|
};
|
|
222
222
|
if (!root?.ownerDocument || typeof capture !== 'function') refuse('pdf_render_unavailable');
|
|
223
|
-
const paper = settings?.papersize || (templatePage ? pageSizeOf(settings) :
|
|
223
|
+
const paper = settings?.papersize || (templatePage ? pageSizeOf(settings) : paperForLanguage(root.ownerDocument.defaultView?.navigator?.language));
|
|
224
224
|
const width = paper.width / 20, height = paper.height / 20;
|
|
225
225
|
const margins = Object.fromEntries(['top', 'right', 'bottom', 'left'].map(side =>
|
|
226
226
|
[side, settings?.geometry ? settings.geometry[side] / 20 : (side === 'top' ? 12 : 14) * 72 / 25.4]));
|
|
@@ -841,7 +841,7 @@ async function writeRasterPdf({pages, textPages, linkPages = [], vectorPages = [
|
|
|
841
841
|
const characters = fallbackCharacters(part, run.carrier); if (!characters.length) continue;
|
|
842
842
|
const {slots, count} = _rapierPrintSlots(part.text, part.rtl);
|
|
843
843
|
const advance = part.width * .75 / count;
|
|
844
|
-
const baseline = decimal(height - margins.top - (part.y + part.height * .8) * .75);
|
|
844
|
+
const baseline = decimal(height - margins.top - (part.baseline ?? part.y + part.height * .8) * .75);
|
|
845
845
|
for (let letter = 0; letter < characters.length; letter++) {
|
|
846
846
|
const size = Math.max(.1, advance * 2 * [...characters[letter].replace(/\n/g, '')].length), scaleY = Math.max(.1, part.height * .75) / size;
|
|
847
847
|
const value = (codes.get(characters[letter]) || codes.get(characters[letter].codePointAt(0))).toString(16).padStart(4, '0');
|
|
@@ -49,6 +49,41 @@ const DIAGRAM_LABEL_CSS = new Set(('color fill stroke background-color font-fami
|
|
|
49
49
|
+ 'text-decoration-thickness text-underline-offset align-items align-content justify-content justify-items flex-direction flex-wrap '
|
|
50
50
|
+ 'width height min-width min-height max-width max-height margin margin-top margin-right margin-bottom margin-left '
|
|
51
51
|
+ 'padding padding-top padding-right padding-bottom padding-left').split(' '));
|
|
52
|
+
const DOCUMENT_STYLE_PROPERTIES = new Set([...DIAGRAM_LABEL_CSS, ...('background display visibility opacity '
|
|
53
|
+
+ 'border border-top border-right border-bottom border-left border-color border-style border-width border-collapse border-spacing '
|
|
54
|
+
+ 'border-top-color border-right-color border-bottom-color border-left-color border-top-width border-right-width border-bottom-width border-left-width '
|
|
55
|
+
+ 'table-layout caption-side empty-cells list-style-type list-style-position direction unicode-bidi hyphens '
|
|
56
|
+
+ 'stroke-width stroke-opacity stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit '
|
|
57
|
+
+ 'fill-opacity fill-rule clip-rule clip-path filter stop-color stop-opacity text-anchor dominant-baseline alignment-baseline '
|
|
58
|
+
+ 'paint-order vector-effect shape-rendering color-interpolation color-interpolation-filters').split(' ')]);
|
|
59
|
+
|
|
60
|
+
function cleanDocumentStyle(tag, value, context, classes = '') {
|
|
61
|
+
if (context === 'raw' && tag === 'span') return typography.style(typography.parseStyle(value));
|
|
62
|
+
const names = classes.split(/\s+/), diagram = tag === 'svg' && names.includes('pele') &&
|
|
63
|
+
(context === 'diagram' || ['render', 'export', 'source'].includes(context) && names.includes('rapier-diagram'));
|
|
64
|
+
const kept = [];
|
|
65
|
+
let start = 0, quote = '', depth = 0;
|
|
66
|
+
const take = end => {
|
|
67
|
+
const part = value.slice(start, end), colon = part.indexOf(':');
|
|
68
|
+
if (colon < 0) return;
|
|
69
|
+
const property = part.slice(0, colon).trim().toLowerCase(), body = part.slice(colon + 1).trim();
|
|
70
|
+
const diagramProperty = diagram && /^--(?:_[a-z0-9_-]+|pele-[a-z0-9-]+)$/.test(property);
|
|
71
|
+
if (!DOCUMENT_STYLE_PROPERTIES.has(property) && !/^--md-[a-z0-9-]+$/.test(property) && !diagramProperty && !(diagram && property === 'font')) return;
|
|
72
|
+
if (!body || /[\\\u0000-\u0008\u000b\u000e-\u001f{}@]|\/\*|\*\//.test(body)) return;
|
|
73
|
+
const withoutLocal = body.replace(/\burl\(\s*(['"]?)#[a-z0-9_.:-]+\1\s*\)/gi, '');
|
|
74
|
+
if (/(?:url|image|image-set|-webkit-image-set|element|paint|attr|expression)\s*\(/i.test(withoutLocal)) return;
|
|
75
|
+
kept.push(part.trim());
|
|
76
|
+
};
|
|
77
|
+
for (let at = 0; at <= value.length; at++) {
|
|
78
|
+
const ch = value[at];
|
|
79
|
+
if (quote) { if (ch === '\\') at++; else if (ch === quote) quote = ''; }
|
|
80
|
+
else if (ch === '"' || ch === "'") quote = ch;
|
|
81
|
+
else if (ch === '(') depth++;
|
|
82
|
+
else if (ch === ')') { if (--depth < 0) return ''; }
|
|
83
|
+
else if ((ch === ';' || at === value.length) && !depth) { take(at); start = at + 1; }
|
|
84
|
+
}
|
|
85
|
+
return quote || depth ? '' : kept.join(';');
|
|
86
|
+
}
|
|
52
87
|
const DIAGRAM_LABEL_SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
|
|
53
88
|
const DIAGRAM_LABEL_HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
|
|
54
89
|
const DIAGRAM_LABEL_MATH_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
|
|
@@ -160,7 +195,8 @@ function createInertHtmlSanitizer({parseFragment, serialize, sanitizeHtml, safeR
|
|
|
160
195
|
if (!_rapierImageDataAttributeAllowed(tag, key, compact, safeRasterDataUrl)) continue;
|
|
161
196
|
} else if (!uri.test(compact)) continue;
|
|
162
197
|
}
|
|
163
|
-
|
|
198
|
+
const cleaned = key === 'style' ? cleanDocumentStyle(tag, value, selected, input.class || '') : value;
|
|
199
|
+
if (key !== 'style' || cleaned) attribs[name] = cleaned;
|
|
164
200
|
}
|
|
165
201
|
if ((tag === 'a' || tag === 'area') && attribs.target?.trim().toLowerCase() === '_blank') attribs.rel = 'noopener noreferrer';
|
|
166
202
|
return {tagName, attribs};
|
|
@@ -254,6 +290,23 @@ function diagramLabelContained(node) {
|
|
|
254
290
|
}
|
|
255
291
|
|
|
256
292
|
function _rapierInstallSanitizeHooks() {
|
|
293
|
+
_rapierInstallDocumentSanitizeHooks();
|
|
294
|
+
DOMPurify.addHook('uponSanitizeElement', node => {
|
|
295
|
+
if (_rapierSanitizeRuntime.context !== 'paste' || !node || !/^(?:P|LI|H[1-6]|SPAN)$/.test(node.nodeName)) return;
|
|
296
|
+
node.removeAttribute('data-rapier-word-level');
|
|
297
|
+
node.removeAttribute('data-rapier-word-marker');
|
|
298
|
+
const style = node.getAttribute('style');
|
|
299
|
+
if (!style || !/mso-list/i.test(style)) return;
|
|
300
|
+
const spec = globalThis.RapierMarkdownSpec;
|
|
301
|
+
if (!spec) return;
|
|
302
|
+
const {wordListLevel, wordListIsMarker} = spec;
|
|
303
|
+
const level = /^(?:P|LI|H[1-6])$/.test(node.nodeName) ? wordListLevel(style) : 0;
|
|
304
|
+
if (level) node.setAttribute('data-rapier-word-level', String(level));
|
|
305
|
+
else if (node.nodeName === 'SPAN' && wordListIsMarker(style)) node.setAttribute('data-rapier-word-marker', '1');
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function _rapierInstallDocumentSanitizeHooks() {
|
|
257
310
|
DOMPurify.addHook('uponSanitizeAttribute', (node, data) => {
|
|
258
311
|
if (!/^(?:src|href|xlink:href)$/i.test(data.attrName || '')) return;
|
|
259
312
|
const compact = String(data.attrValue || '').replace(/[\u0000-\u0020\u00a0]+/g, '');
|
|
@@ -269,7 +322,7 @@ function _rapierInstallSanitizeHooks() {
|
|
|
269
322
|
if (_rapierRemoteContent.allowed || !/^(?:paste|render|raw|export)$/.test(_rapierSanitizeRuntime.context)) return;
|
|
270
323
|
if (!node || node.nodeName !== 'IMG' || node.hasAttribute('data-rapier-remote-src')) return;
|
|
271
324
|
const src = node.getAttribute('src') || '';
|
|
272
|
-
if (!src || !_rapierRemoteSubresourceOrigin(src)) return;
|
|
325
|
+
if (!src || !_rapierRemoteSubresourceOrigin(src) || globalThis.RapierEmbedPictureBase?.vouched?.has(src)) return;
|
|
273
326
|
node._rapierHeldBack = true;
|
|
274
327
|
node.setAttribute('data-rapier-remote-src', src);
|
|
275
328
|
const alt = node.getAttribute('alt');
|
|
@@ -286,39 +339,22 @@ function _rapierInstallSanitizeHooks() {
|
|
|
286
339
|
node.setAttribute('data-rapier-remote-src', href);
|
|
287
340
|
}
|
|
288
341
|
});
|
|
289
|
-
DOMPurify.addHook('uponSanitizeElement', node => {
|
|
290
|
-
if (_rapierSanitizeRuntime.context !== 'paste' || !node || !/^(?:P|LI|H[1-6]|SPAN)$/.test(node.nodeName)) return;
|
|
291
|
-
node.removeAttribute('data-rapier-word-level');
|
|
292
|
-
node.removeAttribute('data-rapier-word-marker');
|
|
293
|
-
const style = node.getAttribute('style');
|
|
294
|
-
if (!style || !/mso-list/i.test(style)) return;
|
|
295
|
-
const spec = globalThis.RapierMarkdownSpec;
|
|
296
|
-
if (!spec) return;
|
|
297
|
-
const {wordListLevel, wordListIsMarker} = spec;
|
|
298
|
-
const level = /^(?:P|LI|H[1-6])$/.test(node.nodeName) ? wordListLevel(style) : 0;
|
|
299
|
-
if (level) node.setAttribute('data-rapier-word-level', String(level));
|
|
300
|
-
else if (node.nodeName === 'SPAN' && wordListIsMarker(style)) node.setAttribute('data-rapier-word-marker', '1');
|
|
301
|
-
});
|
|
302
342
|
DOMPurify.addHook('uponSanitizeAttribute', (node, data) => {
|
|
303
343
|
const value = String(data.attrValue == null ? '' : data.attrValue);
|
|
304
|
-
if (
|
|
305
|
-
data.attrValue =
|
|
344
|
+
if (data.attrName?.toLowerCase() === 'style') {
|
|
345
|
+
data.attrValue = cleanDocumentStyle(node?.localName?.toLowerCase(), value, _rapierSanitizeRuntime.context, node?.getAttribute('class') || '');
|
|
306
346
|
data.keepAttr = !!data.attrValue;
|
|
307
347
|
return;
|
|
308
348
|
}
|
|
309
349
|
if (String(data.attrName || '').toLowerCase() === 'id' && _rapierChromeOwnsId(value)) { data.keepAttr = false; return; }
|
|
310
350
|
if (_rapierRemoteContent.allowed || _rapierSanitizeRuntime.context === 'source') return;
|
|
311
|
-
if (String(data.attrName || '').toLowerCase() === 'style') {
|
|
312
|
-
const cleaned = _rapierStyleWithoutRemoteUrls(value);
|
|
313
|
-
if (cleaned !== value) data.attrValue = cleaned;
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
351
|
const attribute = String(data.attrName || '').toLowerCase();
|
|
317
352
|
if (attribute === 'alt' || attribute === 'title') return;
|
|
318
353
|
if (/^data-rapier-remote-(?:src|alt|title)$/.test(attribute) && node && node._rapierHeldBack === true) { data.forceKeepAttr = true; return; }
|
|
319
354
|
if (attribute.startsWith('data-')) return;
|
|
320
355
|
const element = String(node && node.nodeName || '').toUpperCase();
|
|
321
356
|
if (attribute === 'href' && (element === 'A' || element === 'AREA')) return;
|
|
357
|
+
if (attribute === 'src' && element === 'IMG' && globalThis.RapierEmbedPictureBase?.vouched?.has(value)) return;
|
|
322
358
|
if (_rapierCssPresentationIsRemote(attribute, value) ||
|
|
323
359
|
value.split(/[\s,]+/).some(token => _rapierRemoteSubresourceOrigin(token))) {
|
|
324
360
|
data.keepAttr = false;
|
|
@@ -414,6 +450,13 @@ function _rapierLinkDestinationToConfirm(href, text = '') {
|
|
|
414
450
|
}
|
|
415
451
|
|
|
416
452
|
function _rapierSafeRasterDataUrl(value) {
|
|
453
|
+
if (/^data:image\/svg\+xml,/i.test(value)) {
|
|
454
|
+
try {
|
|
455
|
+
const assets = globalThis.RapierImageAssets;
|
|
456
|
+
return assets.dataImage(value)?.codec === 'image/svg+xml' &&
|
|
457
|
+
_rapierVerifyRasterBytes(assets.decodeDataImageBytes(value).bytes, 'image/svg+xml');
|
|
458
|
+
} catch (_) { return false; }
|
|
459
|
+
}
|
|
417
460
|
const match = RAPIER_RASTER_DATA_URL_RE.exec(value);
|
|
418
461
|
if (!match) return false;
|
|
419
462
|
try {
|
|
@@ -504,7 +547,7 @@ function _rapierVerifyRasterBytes(bytes, mime) {
|
|
|
504
547
|
return false;
|
|
505
548
|
}
|
|
506
549
|
|
|
507
|
-
return {_rapierVerifyRasterBytes, _rapierInstallSanitizeHooks, sanitizeRapierHtml,
|
|
550
|
+
return {_rapierVerifyRasterBytes, _rapierInstallSanitizeHooks, _rapierInstallDocumentSanitizeHooks, sanitizeRapierHtml,
|
|
508
551
|
sanitizeRapierFragment: typeof DOMPurify?.sanitize === 'function' ? sanitizeRapierFragment : undefined,
|
|
509
552
|
diagramLabelStyle, escapeRapierHtmlText, _rapierChromeOwnsId, _rapierLinkDestinationToConfirm, _rapierSafeRasterDataUrl, _rapierCssPresentationIsRemote, _rapierRemoteSubresourceOrigin, _rapierStyleWithoutRemoteUrls, _rapierStylesheetWithoutRemoteUrls, _rapierDropRemoteRules};
|
|
510
553
|
}
|
package/dist/kit/render.mjs
CHANGED
|
@@ -212,14 +212,14 @@ function _rapierTemplatePage(canonical, settings, root, darkPaper = false) {
|
|
|
212
212
|
const image = paragraph.querySelector('img');
|
|
213
213
|
const layout = globalThis.RapierMarkdownLayout.parseLayoutAttribute(paragraph.getAttribute('data-md-layout'));
|
|
214
214
|
const source = image?.getAttribute('src') || '';
|
|
215
|
-
if (isPageBorderLayout(layout) &&
|
|
215
|
+
if (isPageBorderLayout(layout) && globalThis.RapierImageAssets.dataImage(source)) { url = source; paragraph.remove(); break; }
|
|
216
216
|
}
|
|
217
217
|
if (!url) return null;
|
|
218
218
|
const width = paper.width / 15, height = paper.height / 15;
|
|
219
219
|
const margins = ['top', 'right', 'bottom', 'left'].map(side => (settings?.geometry?.[side] ?? 1440) / 15);
|
|
220
220
|
const padding = margins.map(value => value + 'px').join(' ');
|
|
221
221
|
const ground = 'background:var(--rapier-template-border) 0 0/100% 100% no-repeat';
|
|
222
|
-
const shown = darkPaper &&
|
|
222
|
+
const shown = darkPaper && globalThis.RapierImageAssets.dataImage(url)?.codec === 'image/svg+xml' ? _rapierDarkPaperSvgUrl(url, '#fafafa') : url;
|
|
223
223
|
const css = ':root{--rapier-template-border:url("' + url + '")}\n'
|
|
224
224
|
+ '@media screen{' + (shown !== url ? ':root{--rapier-template-border:url("' + shown + '")}' : '') + '.rapier-template-border{display:none}body{background:var(--md-color-inset)}.rapier-page{box-sizing:border-box;width:min(100%,' + width + 'px);min-height:' + height + 'px;margin:0 auto;padding:' + padding
|
|
225
225
|
+ ';background:var(--rapier-template-border) 0 0/100% ' + height + 'px repeat-y,var(--md-color-bg);color:var(--md-color-text)}}\n'
|
|
@@ -235,7 +235,7 @@ function _rapierDarkPaperSvgUrl(url, paperInk) {
|
|
|
235
235
|
const ink = globalThis.RapierImageInspection?.inkForPaper;
|
|
236
236
|
if (typeof ink !== 'function') return url;
|
|
237
237
|
let text;
|
|
238
|
-
try { text = new TextDecoder('utf-8', {fatal: true}).decode(
|
|
238
|
+
try { text = new TextDecoder('utf-8', {fatal: true}).decode(globalThis.RapierImageAssets.decodeDataImageBytes(url).bytes); }
|
|
239
239
|
catch (_) { return url; }
|
|
240
240
|
const inked = ink(text, {dark: true, paperInk});
|
|
241
241
|
if (inked === text) return url;
|
|
@@ -274,7 +274,7 @@ function* _rapierProjectStyledRootSteps(semanticRoot, options) {
|
|
|
274
274
|
for (const token of root.querySelectorAll('.rapier-source-token--softbreak')) { yield;
|
|
275
275
|
if (token.getAttribute('data-rapier-source') !== '%0A') token.replaceWith(...token.childNodes);
|
|
276
276
|
else if (token.previousSibling?.classList?.contains('callout__label')) token.remove();
|
|
277
|
-
else token.replaceWith(root.ownerDocument.createTextNode(' '));
|
|
277
|
+
else token.replaceWith(root.ownerDocument.createTextNode(token.closest('pre') ? '\n' : ' '));
|
|
278
278
|
}
|
|
279
279
|
const metadata = opts.metadata || {};
|
|
280
280
|
|
|
@@ -532,10 +532,10 @@ function _rapierAnnotateExportBoxPolygons(root) {
|
|
|
532
532
|
for (const image of root.querySelectorAll('img[data-rapier-image-layout]')) {
|
|
533
533
|
const layout = globalThis.RapierMarkdownLayout.parseLayoutAttribute(image.getAttribute('data-rapier-image-layout'));
|
|
534
534
|
if (layout?.wrap !== 'box' && layout?.wrap !== 'around') continue;
|
|
535
|
-
const
|
|
536
|
-
if (
|
|
535
|
+
const url = image.getAttribute('src') || '';
|
|
536
|
+
if (globalThis.RapierImageAssets.dataImage(url)?.codec !== 'image/svg+xml') continue;
|
|
537
537
|
let text;
|
|
538
|
-
try { text = new TextDecoder('utf-8', { fatal: true }).decode(
|
|
538
|
+
try { text = new TextDecoder('utf-8', { fatal: true }).decode(globalThis.RapierImageAssets.decodeDataImageBytes(url).bytes); }
|
|
539
539
|
catch (_) { continue; }
|
|
540
540
|
let recipe;
|
|
541
541
|
try { recipe = _rapierDrawReadSVGRecipe(text); } catch (_) { continue; }
|
|
@@ -634,7 +634,7 @@ function _rapierSharedResolve(text, ids, images, definitions) {
|
|
|
634
634
|
let broken = false;
|
|
635
635
|
const src = value => {
|
|
636
636
|
const found = images.get(value);
|
|
637
|
-
if (
|
|
637
|
+
if (!globalThis.RapierImageAssets.dataImage(found)) { broken = true; return null; }
|
|
638
638
|
return found;
|
|
639
639
|
};
|
|
640
640
|
const inline = new RegExp('(\\]\\([ \\t\\r\\n]*(?:<)?)#' + id + '(?=>?[ \\t\\r\\n]*\\)|>?[ \\t\\r\\n]+["\'(])', 'g');
|
|
@@ -896,7 +896,7 @@ async function _rapierRequireOfflinePageImages(root) {
|
|
|
896
896
|
}
|
|
897
897
|
if (!missing.length) return;
|
|
898
898
|
throw Object.assign(new Error('Web page not written; embed these pictures and try again. ' + missing.join('; ')),
|
|
899
|
-
{code: 'offline_image_unavailable'});
|
|
899
|
+
{code: 'offline_image_unavailable', missing});
|
|
900
900
|
}
|
|
901
901
|
|
|
902
902
|
function _rapierCssValueUrls(value) {
|
|
@@ -1741,7 +1741,7 @@ function _rapierArtifactLayoutScript(root, nonce = '', {pictures = true} = {}) {
|
|
|
1741
1741
|
throw new Error('Offline typography requires the complete retained MIT layout factories');
|
|
1742
1742
|
const modules = _rapierArtifactFactoryModules(dependencies, 'layout');
|
|
1743
1743
|
const script = '/* Rapier export layout: SPDX-License-Identifier: MIT */\n' +
|
|
1744
|
-
'/* Pretext
|
|
1744
|
+
'/* Pretext 1.0.0 + main 0d102a5cd933611e86a7e60bda7b4baa5995c6b0\n' + dependencies.license + '\n*/\n' +
|
|
1745
1745
|
'(() => {\nconst modules = Object.create(null);\n' + modules +
|
|
1746
1746
|
(wrapping ? '\n(' + _rapierProjectArtifactLayout.toString() +
|
|
1747
1747
|
')(document.querySelector("main.rapier-page"), modules["spec/md-layout.mjs"], modules["layout/model.mjs"], modules["layout/preparation.mjs"]);\n' : '') +
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
export const WIKI_PREFIX = '?rapier-wiki=';
|
|
3
|
+
export function wikiAddress(target) {
|
|
4
|
+
const at = target.indexOf('#'), path = at < 0 ? target : target.slice(0, at);
|
|
5
|
+
return WIKI_PREFIX + encodeURIComponent(path) + (at < 0 ? '' : '#' + encodeURIComponent(target.slice(at + 1)));
|
|
6
|
+
}
|
|
7
|
+
export function wikiTarget(address) {
|
|
8
|
+
if (typeof address !== 'string' || !address.startsWith(WIKI_PREFIX)) return null;
|
|
9
|
+
try { return decodeURIComponent(address.slice(WIKI_PREFIX.length).split('#')[0]); }
|
|
10
|
+
catch { return null; }
|
|
11
|
+
}
|
|
12
|
+
export function installWikiParser(md, render) {
|
|
13
|
+
md.inline.ruler.before('image', 'rapier_wiki', (state, silent) => {
|
|
14
|
+
if (!state.env?.rapierWikiLinks) return false;
|
|
15
|
+
const start = state.pos, embed = state.src[start] === '!';
|
|
16
|
+
const open = start + (embed ? 1 : 0);
|
|
17
|
+
if (state.linkLevel || state.src.slice(open, open + 2) !== '[[') return false;
|
|
18
|
+
const end = state.src.indexOf(']]', open + 2);
|
|
19
|
+
if (end < 0 || end + 2 > state.posMax || end - start > 4096) return false;
|
|
20
|
+
const body = state.src.slice(open + 2, end);
|
|
21
|
+
if (!body || /[\[\]\n\r\0]/.test(body)) return false;
|
|
22
|
+
const pipe = body.indexOf('|'), target = (pipe < 0 ? body : body.slice(0, pipe)).trim();
|
|
23
|
+
if (!target || /^(?:[a-z][a-z0-9+.-]*:|\/|\\)/i.test(target)) return false;
|
|
24
|
+
if (!silent) {
|
|
25
|
+
const token = state.push('rapier_wiki', '', 0);
|
|
26
|
+
token.content = state.src.slice(start, end + 2);
|
|
27
|
+
token.meta = {target, label: pipe < 0 ? target : body.slice(pipe + 1), embed};
|
|
28
|
+
}
|
|
29
|
+
state.pos = end + 2;
|
|
30
|
+
return true;
|
|
31
|
+
});
|
|
32
|
+
md.renderer.rules.rapier_wiki = (tokens, at, options, env) => render(tokens[at], env);
|
|
33
|
+
}
|
|
34
|
+
|
package/dist/kit/will.d.mts
CHANGED
|
@@ -40,3 +40,6 @@ export function willMarkerOf(line: string): WillMarker | null;
|
|
|
40
40
|
export function terminatorWidth(last: number, prior: number): number;
|
|
41
41
|
/** The text without one trailing line terminator. */
|
|
42
42
|
export function stripOneTerminator(text: string): string;
|
|
43
|
+
/** Judges sequential UTF-16 source splices, including the resulting marker identities and protected text. */
|
|
44
|
+
export function evaluateWill(beforeText: string, splices: {pos: number; removed: string; inserted: string}[]):
|
|
45
|
+
{outcome: 'applied'} | {outcome: 'invalid' | 'refused'; reason: string; rule: string; law?: WillLaw; region?: number};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
import {next, weight, exceptions, maximum} from '../agent/vendor/hyphen-en-us/patterns.js';
|
|
3
3
|
|
|
4
|
+
export const hyphenMinimums = {left: 2, right: 3, word: 5};
|
|
5
|
+
|
|
4
6
|
export function englishCuts(word) {
|
|
5
7
|
if (!/^[A-Za-z]+$/.test(word)) return null;
|
|
6
|
-
if (word.length <
|
|
8
|
+
if (word.length < hyphenMinimums.word) return [];
|
|
7
9
|
const cuts = [];
|
|
8
10
|
let offset = 0, before = 0;
|
|
9
11
|
for (const part of word.match(/[A-Z]+(?![a-z])|[A-Z]?[a-z]+/g)) {
|
|
@@ -11,11 +13,11 @@ export function englishCuts(word) {
|
|
|
11
13
|
for (const at of partCuts(part)) cuts.push(offset+at);
|
|
12
14
|
offset += part.length; before = part.length;
|
|
13
15
|
}
|
|
14
|
-
return cuts.filter(at => at >=
|
|
16
|
+
return cuts.filter(at => at >= hyphenMinimums.left && at <= word.length-hyphenMinimums.right).sort((a, b) => a-b).filter((at, index, all) => at !== all[index-1]);
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
function partCuts(word) {
|
|
18
|
-
if (word.length <
|
|
20
|
+
if (word.length < hyphenMinimums.word) return [];
|
|
19
21
|
const lower = word.toLowerCase(), known = exceptions[lower];
|
|
20
22
|
if (Array.isArray(known)) return known;
|
|
21
23
|
const source = '.'+lower+'.', weights = new Uint8Array(source.length+1);
|
|
@@ -30,7 +32,7 @@ function partCuts(word) {
|
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
34
|
const cuts = [];
|
|
33
|
-
for (let at =
|
|
35
|
+
for (let at = hyphenMinimums.left; at <= word.length-hyphenMinimums.right; at++) if (weights[at+1]&1) cuts.push(at);
|
|
34
36
|
return cuts;
|
|
35
37
|
}
|
|
36
38
|
|