rapier-markdown-kit 1.2.45 → 1.2.48
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 +12 -5
- package/README.md +134 -11
- package/dist/agent/vendor/BROWSER-ENTITIES.json +36 -0
- package/dist/agent/vendor/MARKDOWN-RUNTIME.json +136 -0
- package/dist/agent/vendor/MARKDOWNIT.json +582 -0
- package/dist/agent/vendor/NOTICE.txt +60 -0
- package/dist/agent/vendor/markdown-licenses/entities-LICENSE.txt +11 -0
- package/dist/agent/vendor/markdown-licenses/linkify-it-LICENSE.txt +22 -0
- package/dist/agent/vendor/markdown-licenses/markdown-it-LICENSE.txt +22 -0
- package/dist/agent/vendor/markdown-licenses/mdurl-LICENSE.txt +45 -0
- package/dist/agent/vendor/markdown-licenses/punycode.js-LICENSE-MIT.txt +20 -0
- package/dist/agent/vendor/markdown-licenses/uc.micro-LICENSE.txt +20 -0
- package/dist/agent/vendor/markdown-plugins.json +46 -0
- package/dist/agent/vendor/markdown-plugins.mjs +27 -0
- package/dist/agent/vendor/markdown-runtime.mjs +13 -0
- package/dist/kit/artifact-modules.mjs +19 -0
- package/dist/kit/assets.d.mts +106 -0
- package/dist/kit/export-fonts.mjs +28 -19
- package/dist/kit/index.d.mts +151 -0
- package/dist/kit/layout.d.mts +61 -0
- package/dist/kit/ledger/carried.d.mts +12 -6
- package/dist/kit/ledger/carried.mjs +17 -8
- package/dist/kit/ledger/format.mjs +5 -3
- package/dist/kit/ledger/source.mjs +31 -2
- package/dist/kit/ledger/text-merge.mjs +33 -0
- package/dist/kit/ledger/transport.mjs +61 -2
- package/dist/kit/ledger.d.mts +137 -31
- package/dist/kit/markdown-it.d.mts +2 -2
- package/dist/kit/marks.d.mts +241 -0
- package/dist/kit/marks.mjs +39 -1
- package/dist/kit/model.d.mts +191 -0
- package/dist/kit/pdf-source-digest.mjs +10 -0
- package/dist/kit/pdf-source.mjs +4 -1
- package/dist/kit/pdf-vectors.mjs +3 -2
- package/dist/kit/render-entry.d.mts +654 -0
- package/dist/kit/render-markdown.mjs +14 -8
- package/dist/kit/render-print.mjs +36 -7
- package/dist/kit/render.mjs +16 -14
- package/dist/kit/typography.d.mts +42 -0
- package/dist/kit/will.d.mts +26 -3
- package/dist/layout/initial-range.mjs +18 -0
- package/dist/layout/typography.mjs +2 -18
- package/dist/math/delimiters.mjs +38 -0
- package/dist/math/markdown-rules.mjs +103 -0
- package/dist/packages/rapier-diff/concerns.mjs +2 -2
- package/dist/spec/document-fonts.mjs +62 -16
- package/dist/spec/md-assets.mjs +1 -0
- package/dist/spec/md-comments.mjs +384 -0
- package/dist/spec/md-marks.mjs +9 -0
- package/dist/spec/md-parser-config.mjs +463 -0
- package/dist/spec/md-parser-runtime.mjs +23 -0
- package/package.json +30 -8
|
@@ -715,7 +715,7 @@ function _rapierRenderSemanticRoot(canonical, metadata, options = {}) {
|
|
|
715
715
|
}
|
|
716
716
|
root.innerHTML = sanitizeRapierHtml(rendered, options.page ? 'source' : 'export');
|
|
717
717
|
if (cutAt >= 0) _rapierAppendNestingCut(root, markdown, cutAt);
|
|
718
|
-
_rapierApplyBlockDirection(root);
|
|
718
|
+
_rapierApplyBlockDirection(root, false, options);
|
|
719
719
|
|
|
720
720
|
_rapierAssignHeadingSlugs(root, RAPIER_RENDERED_HEADING_SELECTOR, false, options.carry?.slugs, options.carry?.headingIds);
|
|
721
721
|
_rapierDisambiguateRenderedAnchors(root);
|
|
@@ -791,7 +791,7 @@ async function _rapierRenderSemanticRootAsync(canonical, metadata, options = {})
|
|
|
791
791
|
if (html) append();
|
|
792
792
|
if (cutAt >= 0) _rapierAppendNestingCut(root, markdown, cutAt);
|
|
793
793
|
parsed = tokens = env = labels = null;
|
|
794
|
-
await finishAsync(_rapierSemanticDecorations(root, options.carry), work);
|
|
794
|
+
await finishAsync(_rapierSemanticDecorations(root, options.carry, options), work);
|
|
795
795
|
await pause(work, .6);
|
|
796
796
|
complete = true;
|
|
797
797
|
return root;
|
|
@@ -846,8 +846,8 @@ function _rapierReadPartDecorations(canonical, metadata, options = {}) {
|
|
|
846
846
|
return Object.freeze({headingIds, captionStart});
|
|
847
847
|
}
|
|
848
848
|
|
|
849
|
-
function* _rapierSemanticDecorations(root, carry = null) {
|
|
850
|
-
yield* _rapierApplyBlockDirectionSteps(root); yield;
|
|
849
|
+
function* _rapierSemanticDecorations(root, carry = null, options = {}) {
|
|
850
|
+
yield* _rapierApplyBlockDirectionSteps(root, false, options); yield;
|
|
851
851
|
yield* _rapierAssignHeadingSlugsSteps(root, RAPIER_RENDERED_HEADING_SELECTOR, false, carry?.slugs, carry?.headingIds); yield;
|
|
852
852
|
yield* _rapierDisambiguateRenderedAnchorsSteps(root); yield;
|
|
853
853
|
for (const code of root.querySelectorAll('pre > code')) { _rapierNormalizeCodeElement(code); yield; }
|
|
@@ -1159,14 +1159,20 @@ function _rapierRawHtmlFragment(source, rewrite, parser) {
|
|
|
1159
1159
|
return out;
|
|
1160
1160
|
}
|
|
1161
1161
|
|
|
1162
|
-
function _rapierApplyBlockDirection(root) { return finish(_rapierApplyBlockDirectionSteps(root)); }
|
|
1162
|
+
function _rapierApplyBlockDirection(root, includeRoot = false, options = {}) { return finish(_rapierApplyBlockDirectionSteps(root, includeRoot, options)); }
|
|
1163
1163
|
|
|
1164
|
-
function* _rapierApplyBlockDirectionSteps(root) {
|
|
1164
|
+
function* _rapierApplyBlockDirectionSteps(root, includeRoot = false, {authoredTableDirection = false} = {}) {
|
|
1165
1165
|
if (!root || typeof root.querySelectorAll !== 'function') return;
|
|
1166
|
+
const tableContainers = new Set();
|
|
1167
|
+
if (authoredTableDirection) for (const table of root.querySelectorAll('table')) {
|
|
1168
|
+
for (let parent = table.parentElement; parent && parent !== root && !tableContainers.has(parent); parent = parent.parentElement) {
|
|
1169
|
+
tableContainers.add(parent); yield;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1166
1172
|
const blocks = new Set(_RAPIER_DIR_BLOCKS.split(',')), elements = root.ownerDocument.createTreeWalker(root, 1);
|
|
1167
|
-
for (let el = elements.nextNode(); el; el = elements.nextNode()) {
|
|
1173
|
+
for (let el = includeRoot ? root : elements.nextNode(); el; el = elements.nextNode()) {
|
|
1168
1174
|
yield;
|
|
1169
|
-
if (!blocks.has(el.localName)) continue;
|
|
1175
|
+
if (!blocks.has(el.localName) || tableContainers.has(el)) continue;
|
|
1170
1176
|
let literal = false;
|
|
1171
1177
|
for (let parent = el.parentElement; parent; parent = parent.parentElement)
|
|
1172
1178
|
if (parent.localName === 'pre' || parent.localName === 'code') { literal = true; break; }
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
import {collectPdfVectors} from './pdf-vectors.mjs';
|
|
3
3
|
import {collectPdfGlyphs} from './pdf-glyphs.mjs';
|
|
4
4
|
import {pdfFontSources, waitForFonts} from './export-fonts.mjs';
|
|
5
|
-
import {
|
|
5
|
+
import {printTypography} from './artifact-modules.mjs';
|
|
6
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';
|
|
10
|
+
import {PDF_SOURCE_DIGEST, pdfSourceDigest} from './pdf-source-digest.mjs';
|
|
10
11
|
const RAPIER_WILL_SENTINEL_MARK = '';
|
|
11
12
|
// SPDX-License-Identifier: MIT
|
|
12
13
|
function createPrintRenderer(runtime) {
|
|
@@ -229,7 +230,8 @@ async function _rapierWritePrintPdf({root, css, fontCss = '', canonical, bom = f
|
|
|
229
230
|
if (bodyWidth < 1 || bodyHeight < 1) refuse('pdf_page_geometry_invalid');
|
|
230
231
|
const tiled = bodyWidth > CAPTURE_EDGE || bodyHeight > CAPTURE_EDGE || bodyWidth * bodyHeight > CAPTURE_PIXELS;
|
|
231
232
|
const frame = document.createElement('iframe');
|
|
232
|
-
let vectors, backdrop = null, nativeGlyphs, filmSession;
|
|
233
|
+
let vectors, backdrop = null, nativeGlyphs, filmSession, typography;
|
|
234
|
+
const omitted = [];
|
|
233
235
|
let rejectAbort;
|
|
234
236
|
const aborted = new Promise((_, reject) => { rejectAbort = reject; });
|
|
235
237
|
aborted.catch(() => {});
|
|
@@ -281,7 +283,18 @@ async function _rapierWritePrintPdf({root, css, fontCss = '', canonical, bom = f
|
|
|
281
283
|
main.getBoundingClientRect();
|
|
282
284
|
if (fontCss) await step(waitForFonts(main, {fontCss, css, settings}));
|
|
283
285
|
await step(into.fonts.ready);
|
|
284
|
-
for (const image of main.querySelectorAll('img')) {
|
|
286
|
+
for (const image of main.querySelectorAll('img')) {
|
|
287
|
+
image.loading = 'eager';
|
|
288
|
+
try { await step(image.decode()); }
|
|
289
|
+
catch (error) {
|
|
290
|
+
if (error?.name !== 'EncodingError') throw error;
|
|
291
|
+
const source = image.getAttribute('src') || '';
|
|
292
|
+
omitted.push(source.startsWith('data:') ? image.getAttribute('alt') || 'a picture' : source.slice(0, 120));
|
|
293
|
+
const holder = image.closest('p,figure');
|
|
294
|
+
image.remove();
|
|
295
|
+
if (holder && !holder.textContent.trim() && !holder.querySelector('img,svg,math,table,pre')) holder.remove();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
285
298
|
wrapMasks(main);
|
|
286
299
|
check();
|
|
287
300
|
for (const node of [main, ...main.querySelectorAll('*')]) {
|
|
@@ -303,7 +316,8 @@ async function _rapierWritePrintPdf({root, css, fontCss = '', canonical, bom = f
|
|
|
303
316
|
main.style.setProperty(name, value, 'important');
|
|
304
317
|
globalThis.RapierMath?.fit(main);
|
|
305
318
|
applyPrintCapitals(main);
|
|
306
|
-
|
|
319
|
+
typography = printTypography(main, globalThis.RapierArtifactLayoutDependencies);
|
|
320
|
+
typography?.apply(main, {all: true});
|
|
307
321
|
ink?.(main);
|
|
308
322
|
const count = Math.max(1, Math.ceil((main.scrollWidth - 1) / bodyWidth));
|
|
309
323
|
if (!Number.isSafeInteger(count)) refuse('pdf_page_geometry_invalid');
|
|
@@ -421,9 +435,9 @@ async function _rapierWritePrintPdf({root, css, fontCss = '', canonical, bom = f
|
|
|
421
435
|
const bytes = await writeRasterPdf({pages, textPages, linkPages, vectorPages: vectors.pages, canonical, bom, filename, title, font, width, height, margins, backdrop: backdrop?.pages[0] || null,
|
|
422
436
|
pixelWidth: bodyWidth, pixelHeight: bodyHeight, pageNumbers: settings?.pagestyle === 'plain', maxBytes, check});
|
|
423
437
|
check();
|
|
424
|
-
return {bytes, pages: count, painted: nativeGlyphs.painted};
|
|
438
|
+
return {bytes, pages: count, painted: nativeGlyphs.painted, omitted};
|
|
425
439
|
} catch (error) { throw spoken(error); }
|
|
426
|
-
finally { filmSession?.close(); backdrop?.restore(); vectors?.restore(); nativeGlyphs?.restore(); signal?.removeEventListener('abort', abort); frame.remove(); }
|
|
440
|
+
finally { filmSession?.close(); backdrop?.restore(); vectors?.restore(); nativeGlyphs?.restore(); typography?.retire(frame.contentDocument); signal?.removeEventListener('abort', abort); frame.remove(); }
|
|
427
441
|
}
|
|
428
442
|
|
|
429
443
|
async function _rapierPrintTextPages(main, pageWidth, count, check, origin = main.getBoundingClientRect(), nativeNodes = null, markerNodes = null) {
|
|
@@ -442,6 +456,18 @@ async function _rapierPrintTextPages(main, pageWidth, count, check, origin = mai
|
|
|
442
456
|
}
|
|
443
457
|
const marker = markerNodes?.get(node);
|
|
444
458
|
if (marker && marker.page < count) pages[marker.page].push({...marker, block: node, carrier: null, marker: true});
|
|
459
|
+
const taskItem = node.nodeType === 1 && node.matches('input[type="checkbox"]') && node.closest('li');
|
|
460
|
+
const taskParent = taskItem && node.parentElement;
|
|
461
|
+
if (taskItem && taskParent.firstElementChild === node && (taskParent === taskItem || taskParent.localName === 'p' && taskParent.parentElement === taskItem && taskItem.firstElementChild === taskParent)) {
|
|
462
|
+
const box = node.getBoundingClientRect(), style = into.defaultView.getComputedStyle(node);
|
|
463
|
+
if (box.width > 0 && box.height > 0 && style.visibility !== 'hidden' && style.visibility !== 'collapse') {
|
|
464
|
+
const page = Math.max(0, Math.min(count - 1, Math.floor((box.left - origin.left + .01) / pageWidth)));
|
|
465
|
+
const size = parseFloat(style.fontSize) || 16, width = Math.min(size, box.width), height = Math.min(size, box.height);
|
|
466
|
+
const text = node.checked ? '[x] ' : '[ ] ', part = {text, x: box.left - origin.left - page * pageWidth + (box.width - width) / 2,
|
|
467
|
+
y: box.top - origin.top + (box.height - height) / 2, width, height};
|
|
468
|
+
pages[page].push({...part, parts: [part], block: taskItem, carrier: null, marker: true});
|
|
469
|
+
}
|
|
470
|
+
}
|
|
445
471
|
const formula = node.nodeType === 1 && node.matches('.math-rendered[data-math-src]') && node.querySelector('svg');
|
|
446
472
|
if (node.nodeType === 1 && !formula || node.nodeType === 3 && element.closest('.math-rendered[data-math-src]')) continue;
|
|
447
473
|
let value;
|
|
@@ -885,7 +911,8 @@ async function writeRasterPdf({pages, textPages, linkPages = [], vectorPages = [
|
|
|
885
911
|
cursor += (id + ' 0 obj\n').length + objects[id].length + 8;
|
|
886
912
|
}
|
|
887
913
|
const xrefAt = cursor;
|
|
888
|
-
const
|
|
914
|
+
const table = 'xref\n0 ' + objects.length + '\n0000000000 65535 f \n' + offsets.slice(1).map(at => String(at).padStart(10, '0') + ' 00000 n \n').join('') + 'trailer\n<< /Size ' + objects.length + ' /Root 1 0 R /Info ' + info + ' 0 R ' + PDF_SOURCE_DIGEST + ' >>\nstartxref\n' + xrefAt + '\n%%EOF\n';
|
|
915
|
+
const xref = encode(table), digestAt = xrefAt + table.indexOf(PDF_SOURCE_DIGEST) + PDF_SOURCE_DIGEST.indexOf('<') + 1;
|
|
889
916
|
if (cursor + xref.length > maxBytes) fail('export_too_large');
|
|
890
917
|
const bytes = new Uint8Array(cursor + xref.length);
|
|
891
918
|
bytes.set(header, 0);
|
|
@@ -899,6 +926,8 @@ async function writeRasterPdf({pages, textPages, linkPages = [], vectorPages = [
|
|
|
899
926
|
bytes.set(suffix, at); at += suffix.length;
|
|
900
927
|
}
|
|
901
928
|
bytes.set(xref, at);
|
|
929
|
+
bytes.set(encode(await pdfSourceDigest(bytes, digestAt, bytes.length - 1)), digestAt);
|
|
930
|
+
check();
|
|
902
931
|
return bytes;
|
|
903
932
|
}
|
|
904
933
|
export {createPrintRenderer, applyPrintCapitals, RAPIER_WILL_SENTINEL_MARK as sentinel};
|
package/dist/kit/render.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import {createLineMeasurer, linesHeight} from '../layout/model.mjs';
|
|
|
9
9
|
import {markPrintCapitals, createPrintCapitals, followingPrintProse} from './print-capitals.mjs';
|
|
10
10
|
import {freezePublishedInk} from './render-ink.mjs';
|
|
11
11
|
import {isTemplatePage, isPageBorderLayout} from '../spec/template-page.mjs';
|
|
12
|
+
import {_rapierArtifactFactoryModules} from './artifact-modules.mjs';
|
|
12
13
|
export function positionNativeDiagram(drawing) {
|
|
13
14
|
const surface=drawing?.closest?.('.diagram-block,.diagram-live-preview');
|
|
14
15
|
if(!surface?.clientWidth)return;
|
|
@@ -49,8 +50,7 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
49
50
|
const opts = options || {};
|
|
50
51
|
const context = providedContext || _rapierBuildInterchangeContext(opts);
|
|
51
52
|
const preference = _rapierArtifactPreference('theme', ['system', 'light', 'dark']);
|
|
52
|
-
const theme = opts.print ? 'light' : preference
|
|
53
|
-
? (globalThis.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : preference;
|
|
53
|
+
const theme = opts.print ? 'light' : preference;
|
|
54
54
|
const metadata = context.metadata;
|
|
55
55
|
const base = context.baseName;
|
|
56
56
|
const work = context.work || opts.work;
|
|
@@ -113,8 +113,8 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
113
113
|
const styledRoot = work ? await _rapierProjectStyledRootAsync(context.semanticRoot, styledOptions)
|
|
114
114
|
: _rapierProjectStyledRoot(context.semanticRoot, styledOptions);
|
|
115
115
|
styledRoot.setAttribute('lang', language);
|
|
116
|
-
const
|
|
117
|
-
const templatePage = _rapierTemplatePage(context.canonical, settings, styledRoot,
|
|
116
|
+
const paperTheme = _rapierArtifactPreference('invertImageAlpha', ['true', 'false']) !== 'false' ? theme : 'light';
|
|
117
|
+
const templatePage = _rapierTemplatePage(context.canonical, settings, styledRoot, paperTheme);
|
|
118
118
|
if (completePage && typeof _rapierFinalizeExportCode === 'function') await _rapierFinalizeExportCode(styledRoot);
|
|
119
119
|
if (!opts.print) _rapierAnnotateExportBoxPolygons(styledRoot);
|
|
120
120
|
if (completePage || opts.print) {
|
|
@@ -204,7 +204,7 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
204
204
|
};
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
function _rapierTemplatePage(canonical, settings, root,
|
|
207
|
+
function _rapierTemplatePage(canonical, settings, root, paperTheme = 'light') {
|
|
208
208
|
if (!isTemplatePage(canonical)) return null;
|
|
209
209
|
const paper = pageSizeOf(settings);
|
|
210
210
|
let url = '';
|
|
@@ -219,9 +219,11 @@ function _rapierTemplatePage(canonical, settings, root, darkPaper = false) {
|
|
|
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 =
|
|
222
|
+
const shown = paperTheme !== 'light' && globalThis.RapierImageAssets.dataImage(url)?.codec === 'image/svg+xml' ? _rapierDarkPaperSvgUrl(url, '#fafafa') : url;
|
|
223
|
+
const screenInk = shown !== url ? '@media screen' + (paperTheme === 'system' ? ' and (prefers-color-scheme:dark)' : '')
|
|
224
|
+
+ '{:root{--rapier-template-border:url("' + shown + '")}}\n' : '';
|
|
223
225
|
const css = ':root{--rapier-template-border:url("' + url + '")}\n'
|
|
224
|
-
+ '@media screen{
|
|
226
|
+
+ screenInk + '@media screen{.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
227
|
+ ';background:var(--rapier-template-border) 0 0/100% ' + height + 'px repeat-y,var(--md-color-bg);color:var(--md-color-text)}}\n'
|
|
226
228
|
+ '@media screen and (max-width:' + width + 'px){.rapier-page{min-height:auto;aspect-ratio:' + width + '/' + height + ';padding:'
|
|
227
229
|
+ margins.map(value => 'min(' + value + 'px,' + Number((value / width * 100).toFixed(4)) + '%)').join(' ') + ';background-size:100% auto,auto}}\n'
|
|
@@ -347,9 +349,14 @@ function* _rapierProjectStyledRootSteps(semanticRoot, options) {
|
|
|
347
349
|
yield;
|
|
348
350
|
cache.innerHTML = sanitizeRapierHtml(cache.innerHTML, 'export');
|
|
349
351
|
}
|
|
350
|
-
return root;
|
|
351
352
|
}
|
|
352
|
-
|
|
353
|
+
const admitted = opts.sanitized ? root : yield* cleanTree(root, sanitizeRapierHtml, 'export');
|
|
354
|
+
for (const list of admitted.querySelectorAll('ol[start]')) {
|
|
355
|
+
yield;
|
|
356
|
+
const start = list.getAttribute('start');
|
|
357
|
+
if (/^-?\d{1,9}$/.test(start)) list.style.setProperty('counter-reset', 'rapier-ol ' + (Number(start) - 1));
|
|
358
|
+
}
|
|
359
|
+
return admitted;
|
|
353
360
|
}
|
|
354
361
|
|
|
355
362
|
function _rapierDocxRenderStyles(root) {
|
|
@@ -1279,11 +1286,6 @@ function _rapierImageOpenRunsList(tokens) {
|
|
|
1279
1286
|
return list;
|
|
1280
1287
|
}
|
|
1281
1288
|
|
|
1282
|
-
function _rapierArtifactFactoryModules(dependencies, group) {
|
|
1283
|
-
const paths = dependencies.groups && dependencies.groups[group] ? dependencies.groups[group] : Object.keys(dependencies.factories);
|
|
1284
|
-
return paths.map(path => 'modules[' + JSON.stringify(path) + '] = (' + Function.prototype.toString.call(dependencies.factories[path]) + ')();').join('\n');
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
1289
|
function _rapierProjectArtifactLayout(root, metadata, geometry, preparation) {
|
|
1288
1290
|
if (!root || !metadata || !geometry || !preparation) return null;
|
|
1289
1291
|
const doc = root.ownerDocument, view = doc.defaultView;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import type {RichTextItem, PreparationOptions, Rect, SlotsForBand, SlotSequence, FlowPlan,
|
|
3
|
+
FlowFragment, FlowSlot} from './model.mjs';
|
|
4
|
+
|
|
5
|
+
export interface TypographyApplyResult {
|
|
6
|
+
changed: number;
|
|
7
|
+
released: number;
|
|
8
|
+
pending: boolean;
|
|
9
|
+
prepared: number;
|
|
10
|
+
}
|
|
11
|
+
export interface InitialRange {start: number; end: number; text: string}
|
|
12
|
+
export interface TypographySlotInput {
|
|
13
|
+
width: number;
|
|
14
|
+
top: number;
|
|
15
|
+
obstacles: readonly Rect[];
|
|
16
|
+
lineHeight: number;
|
|
17
|
+
minWidth: number;
|
|
18
|
+
slotsForBand: SlotsForBand;
|
|
19
|
+
slotSequence: SlotSequence;
|
|
20
|
+
columnsFilled(slots: readonly FlowSlot[], placed: readonly FlowSlot[]): boolean;
|
|
21
|
+
justify: boolean;
|
|
22
|
+
hyphenate: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface DerivedFlowFragment extends FlowFragment {
|
|
25
|
+
derive: true;
|
|
26
|
+
glyph?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface TypographyController {
|
|
29
|
+
/** Pending work requires a subsequent call after the host yields. */
|
|
30
|
+
apply(root: Element | Document | DocumentFragment, options?: {all?: boolean; editing?: boolean}): TypographyApplyResult;
|
|
31
|
+
clear(root: Element | Document | DocumentFragment, options?: {forget?: boolean; keepSelection?: boolean}): void;
|
|
32
|
+
clearSelection(root: Element | Document | DocumentFragment): number;
|
|
33
|
+
firstLineEnd(paragraph: Element | null | undefined): number | null;
|
|
34
|
+
initialRange(input: string | Pick<Node, 'textContent'> | null | undefined): InitialRange | null;
|
|
35
|
+
protectInitial(root: Element | Document | DocumentFragment): void;
|
|
36
|
+
invalidateFonts(document?: Document | null): number;
|
|
37
|
+
retire(root: Element | Document | DocumentFragment, options?: {documentChanged?: boolean}): void;
|
|
38
|
+
slotPlan(paragraph: Element, items: readonly RichTextItem[],
|
|
39
|
+
options: Pick<PreparationOptions, 'language' | 'wordBreak'>, input: TypographySlotInput): FlowPlan<DerivedFlowFragment> | null;
|
|
40
|
+
}
|
|
41
|
+
/** Creates an independent disposable DOM projection owner; it never changes Markdown source. */
|
|
42
|
+
export function createTypography(): TypographyController;
|
package/dist/kit/will.d.mts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Will/1: permissions a Markdown document gives agents, as HTML comments on their own lines.
|
|
3
3
|
// <!-- will/1 keep --> ... <!-- /will --> keep: no change; append: add after the region's text only; edit: any change
|
|
4
4
|
export type WillLaw = 'keep' | 'append' | 'edit';
|
|
5
|
-
export type WillFault = 'malformed_marker' | 'unpaired_marker';
|
|
5
|
+
export type WillFault = 'malformed_marker' | 'unpaired_marker' | 'unknown_version' | 'unknown_law' | 'intent_over_bound';
|
|
6
6
|
/** A paired region; `start` to `end` is its text between the marker lines (UTF-16 offsets). */
|
|
7
7
|
export interface WillRegion {
|
|
8
8
|
index: number; law: WillLaw; intent: string | null;
|
|
@@ -40,6 +40,29 @@ 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
|
+
/** A sequential source edit in UTF-16 coordinates; removed must match the current source. */
|
|
44
|
+
export interface WillSplice {pos: number; removed: string; inserted: string;}
|
|
45
|
+
export interface WillRestoreOptions {
|
|
46
|
+
/** Exact splice objects the host has proved undo its own earlier act; text equality is not authority. */
|
|
47
|
+
restores?: readonly WillSplice[] | null;
|
|
48
|
+
}
|
|
49
|
+
export interface WillTransitionOptions extends WillRestoreOptions {
|
|
50
|
+
/** Optional output map populated with admitted contiguous append-tail inverse rows. */
|
|
51
|
+
restored?: Map<number, WillSplice[]> | null;
|
|
52
|
+
}
|
|
53
|
+
export type WillTransitionRefusal =
|
|
54
|
+
| {law: 'keep'; rule: 'before_faulted' | 'result_faulted'; faults: {mode: WillFault; line: number}[]; start: number; end: number}
|
|
55
|
+
| {law: WillLaw; rule: 'marker_span_touched'; editIndex: number; region: number; start: number; end: number}
|
|
56
|
+
| {law: 'keep'; rule: 'marker_sequence_mismatch'; start: number; end: number}
|
|
57
|
+
| {law: 'keep' | 'append'; rule: 'law_violated'; region: number; start: number; end: number};
|
|
58
|
+
/** Maps a resulting UTF-16 offset back through sequential rows; -1 means it lies in inserted text. */
|
|
59
|
+
export function willOffsetBefore(rows: readonly WillSplice[], offset: number): number;
|
|
60
|
+
/** Checks marker identity and body laws after the host has proved the sequential splices. Null admits the transition. */
|
|
61
|
+
export function willTransition(beforeText: string, afterText: string, splices: readonly WillSplice[],
|
|
62
|
+
markerSplices?: readonly WillSplice[], options?: WillTransitionOptions): WillTransitionRefusal | null;
|
|
63
|
+
export type WillEvaluation =
|
|
64
|
+
| {outcome: 'applied'}
|
|
65
|
+
| {outcome: 'invalid'; reason: 'precondition'; rule: 'invalid_unicode' | 'malformed_splice' | 'out_of_range_splice' | 'source_mismatch'}
|
|
66
|
+
| ({outcome: 'refused'; reason: 'document_law'} & WillTransitionRefusal);
|
|
43
67
|
/** Judges sequential UTF-16 source splices, including the resulting marker identities and protected text. */
|
|
44
|
-
export function evaluateWill(beforeText: string, splices:
|
|
45
|
-
{outcome: 'applied'} | {outcome: 'invalid' | 'refused'; reason: string; rule: string; law?: WillLaw; region?: number};
|
|
68
|
+
export function evaluateWill(beforeText: string, splices: readonly WillSplice[], options?: WillRestoreOptions): WillEvaluation;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
let graphemes;
|
|
3
|
+
export const segment = text => (graphemes ||= new Intl.Segmenter(undefined, {granularity: 'grapheme'})).segment(text);
|
|
4
|
+
export const opening = /^[\p{Ps}\p{Pi}\p{Pf}"']+$/u;
|
|
5
|
+
|
|
6
|
+
export function initialRange(input) {
|
|
7
|
+
const text = typeof input === 'string' ? input : input?.textContent;
|
|
8
|
+
if (typeof text !== 'string') return null;
|
|
9
|
+
const start = /^\s*/u.exec(text)[0].length;
|
|
10
|
+
if (start === text.length) return null;
|
|
11
|
+
let end = start;
|
|
12
|
+
for (const part of segment(text.slice(start))) {
|
|
13
|
+
if (/^\s/u.test(part.segment)) break;
|
|
14
|
+
end = start + part.index + part.segment.length;
|
|
15
|
+
if (!opening.test(part.segment)) break;
|
|
16
|
+
}
|
|
17
|
+
return end > start ? {start, end, text: text.slice(start, end)} : null;
|
|
18
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
import {createPreparationScope, supportsRichStyle, captureFontAuthority} from './preparation.mjs';
|
|
3
3
|
import {createParagraphFitter} from './paragraph-fit.mjs';
|
|
4
|
+
import {initialRange, segment, opening} from './initial-range.mjs';
|
|
4
5
|
|
|
5
6
|
export function createTypography() {
|
|
6
7
|
const fitter = createParagraphFitter();
|
|
@@ -16,25 +17,8 @@ export function createTypography() {
|
|
|
16
17
|
if (!value) { value = createPreparationScope(document); documents.set(document, value); }
|
|
17
18
|
return value;
|
|
18
19
|
}
|
|
19
|
-
let graphemes;
|
|
20
|
-
const segment = text => (graphemes ||= new Intl.Segmenter(undefined, {granularity: 'grapheme'})).segment(text);
|
|
21
|
-
const opening = /^[\p{Ps}\p{Pi}\p{Pf}"']+$/u;
|
|
22
20
|
const closing = /^[\p{Pe}\p{Pi}\p{Pf}"'.,;:!?]+$/u;
|
|
23
21
|
|
|
24
|
-
function initialRange(input) {
|
|
25
|
-
const text = typeof input === 'string' ? input : input?.textContent;
|
|
26
|
-
if (typeof text !== 'string') return null;
|
|
27
|
-
const start = /^\s*/u.exec(text)[0].length;
|
|
28
|
-
if (start === text.length) return null;
|
|
29
|
-
let end = start;
|
|
30
|
-
for (const part of segment(text.slice(start))) {
|
|
31
|
-
if (/^\s/u.test(part.segment)) break;
|
|
32
|
-
end = start + part.index + part.segment.length;
|
|
33
|
-
if (!opening.test(part.segment)) break;
|
|
34
|
-
}
|
|
35
|
-
return end > start ? {start, end, text: text.slice(start, end)} : null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
22
|
const flowFloat = '.rapier-flow-float';
|
|
39
23
|
const own = paragraph => [paragraph, ...[...paragraph.querySelectorAll('*')].filter(element => !element.closest(flowFloat))];
|
|
40
24
|
|
|
@@ -770,7 +754,7 @@ export const apply = typography.apply;
|
|
|
770
754
|
export const clear = typography.clear;
|
|
771
755
|
export const clearSelection = typography.clearSelection;
|
|
772
756
|
export const firstLineEnd = typography.firstLineEnd;
|
|
773
|
-
export
|
|
757
|
+
export {initialRange};
|
|
774
758
|
export const protectInitial = typography.protectInitial;
|
|
775
759
|
export const invalidateFonts = typography.invalidateFonts;
|
|
776
760
|
export const retire = typography.retire;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
export function createMathMatcher(source, {singleDollar = false, brackets = true} = {}) {
|
|
3
|
+
if (typeof source !== 'string') throw new TypeError('Math boundaries require source text.');
|
|
4
|
+
const markers = new Map(), next = Object.create(null);
|
|
5
|
+
let slashes = 0, line = 0;
|
|
6
|
+
for (let at = 0; at < source.length;) {
|
|
7
|
+
const char = source[at];
|
|
8
|
+
if (char === '\\') { slashes++; at++; continue; }
|
|
9
|
+
if (char === '\n' || char === '\r') line++;
|
|
10
|
+
if (char === '$') {
|
|
11
|
+
let end = at + 1;
|
|
12
|
+
while (source[end] === '$') end++;
|
|
13
|
+
const count = end - at;
|
|
14
|
+
if (!(slashes & 1) && (count === 2 || count === 1 && singleDollar))
|
|
15
|
+
markers.set(at, {at, marker: count === 2 ? '$$' : '$', line});
|
|
16
|
+
at = end;
|
|
17
|
+
} else {
|
|
18
|
+
if (brackets && slashes & 1 && '()[]'.includes(char))
|
|
19
|
+
markers.set(at - 1, {at: at - 1, marker: '\\' + char, line});
|
|
20
|
+
at++;
|
|
21
|
+
}
|
|
22
|
+
slashes = 0;
|
|
23
|
+
}
|
|
24
|
+
for (const entry of [...markers.values()].reverse()) {
|
|
25
|
+
const closing = entry.marker === '\\(' ? '\\)' : entry.marker === '\\[' ? '\\]' : entry.marker;
|
|
26
|
+
entry.close = next[closing];
|
|
27
|
+
next[entry.marker] = entry;
|
|
28
|
+
}
|
|
29
|
+
return function match(start, limit = source.length) {
|
|
30
|
+
const open = markers.get(start), close = open?.close;
|
|
31
|
+
if (!close || open.marker === '\\)' || open.marker === '\\]' || close.at + close.marker.length > limit) return null;
|
|
32
|
+
const bodyStart = start + open.marker.length, bodyEnd = close.at;
|
|
33
|
+
if (open.marker === '$' && (open.line !== close.line || bodyStart === bodyEnd ||
|
|
34
|
+
/\s/.test(source[bodyStart]) || /\s/.test(source[bodyEnd - 1]) || /[0-9]/.test(source[bodyEnd + 1] || ''))) return null;
|
|
35
|
+
return {start, end: bodyEnd + close.marker.length, bodyStart, bodyEnd,
|
|
36
|
+
open: open.marker, close: close.marker, displayMode: open.marker === '$$' || open.marker === '\\['};
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import {createMathMatcher} from './delimiters.mjs';
|
|
3
|
+
|
|
4
|
+
export function installMathRules(md, {singleDollar = false, brackets = true, ruleName = 'rapier_math', inLink = false} = {}) {
|
|
5
|
+
const authored = new WeakMap();
|
|
6
|
+
md.core.ruler.before('normalize', ruleName + '_source', state => {
|
|
7
|
+
authored.set(state.tokens, {source: state.src});
|
|
8
|
+
});
|
|
9
|
+
md.core.ruler.after('block', ruleName + '_source_release', state => authored.delete(state.tokens));
|
|
10
|
+
const tokenize = md.block.tokenize;
|
|
11
|
+
md.block.tokenize = function(state, startLine, endLine) {
|
|
12
|
+
const first = state.tokens.length;
|
|
13
|
+
try { return tokenize.call(this, state, startLine, endLine); }
|
|
14
|
+
finally {
|
|
15
|
+
for (let index = first; index < state.tokens.length; index++) {
|
|
16
|
+
const token = state.tokens[index];
|
|
17
|
+
if (token.type !== 'fence' || !token.map || token.meta?.mathSource !== undefined ||
|
|
18
|
+
String(token.info || '').trim().split(/\s+/)[0].toLowerCase() !== 'math') continue;
|
|
19
|
+
const held = authored.get(state.tokens), source = held?.source ?? state.src;
|
|
20
|
+
let value;
|
|
21
|
+
if (!state.rapierSourceSlice && source === state.src) value = state.getLines(token.map[0], token.map[1], state.blkIndent, false);
|
|
22
|
+
else {
|
|
23
|
+
if (!state.rapierSourceSlice) {
|
|
24
|
+
const starts = text => [0, ...Array.from(text.matchAll(/\r\n?|\n/g), match => match.index + match[0].length)];
|
|
25
|
+
held.rawLines ||= starts(source);
|
|
26
|
+
held.lines ||= starts(state.src);
|
|
27
|
+
}
|
|
28
|
+
const view = Object.create(state), parts = [];
|
|
29
|
+
view.bMarks = []; view.eMarks = [];
|
|
30
|
+
for (let line = token.map[0]; line < token.map[1]; line++) {
|
|
31
|
+
const shift = state.rapierSourceSlice ? 0 : held.rawLines[line] - held.lines[line];
|
|
32
|
+
view.src = state.rapierSourceSlice
|
|
33
|
+
? state.rapierSourceSlice(state.bMarks[line], state.eMarks[line])
|
|
34
|
+
: source.slice(state.bMarks[line] + shift, state.eMarks[line] + shift);
|
|
35
|
+
view.bMarks[line] = 0; view.eMarks[line] = view.src.length;
|
|
36
|
+
parts.push(view.getLines(line, line + 1, state.blkIndent, false));
|
|
37
|
+
if (line + 1 < token.map[1]) parts.push(state.rapierSourceSlice
|
|
38
|
+
? state.rapierSourceSlice(state.eMarks[line], state.eMarks[line] + 1)
|
|
39
|
+
: source.slice(state.eMarks[line] + shift, held.rawLines[line + 1]));
|
|
40
|
+
}
|
|
41
|
+
value = parts.join('');
|
|
42
|
+
}
|
|
43
|
+
token.meta = {...token.meta, mathSource: value};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const renderText = md.renderer.renderInlineAsText;
|
|
48
|
+
md.renderer.renderInlineAsText = function(tokens, options, env) {
|
|
49
|
+
return renderText.call(this, tokens.map(token =>
|
|
50
|
+
token.type === 'math_inline' || token.type === 'math_block'
|
|
51
|
+
? {...token, type: 'text', content: token.meta?.mathSource ?? token.content}
|
|
52
|
+
: token), options, env);
|
|
53
|
+
};
|
|
54
|
+
const matchers = new WeakMap();
|
|
55
|
+
function match(state, at, limit) {
|
|
56
|
+
let entry = matchers.get(state);
|
|
57
|
+
if (!entry || entry.source !== state.src) {
|
|
58
|
+
entry = {source: state.src, match: createMathMatcher(state.src, {singleDollar, brackets})};
|
|
59
|
+
matchers.set(state, entry);
|
|
60
|
+
}
|
|
61
|
+
return entry.match(at, limit);
|
|
62
|
+
}
|
|
63
|
+
md.inline.ruler.before('escape', ruleName, (state, silent) => {
|
|
64
|
+
if (!inLink && state.linkLevel > 0) return false;
|
|
65
|
+
if (state.src[state.pos] !== '$' && state.src[state.pos] !== '\\') return false;
|
|
66
|
+
const span = match(state, state.pos, state.posMax);
|
|
67
|
+
if (!span) return false;
|
|
68
|
+
if (!silent) {
|
|
69
|
+
const token = state.push(span.displayMode ? 'math_block' : 'math_inline', '', 0);
|
|
70
|
+
token.markup = span.open;
|
|
71
|
+
token.content = state.src.slice(span.bodyStart, span.bodyEnd);
|
|
72
|
+
token.meta = {mathSource: state.src.slice(span.start, span.end)};
|
|
73
|
+
}
|
|
74
|
+
state.pos = span.end;
|
|
75
|
+
return true;
|
|
76
|
+
});
|
|
77
|
+
md.block.ruler.before('fence', ruleName, (state, startLine, endLine, silent) => {
|
|
78
|
+
if (state.sCount[startLine] - state.blkIndent >= 4) return false;
|
|
79
|
+
const start = state.bMarks[startLine] + state.tShift[startLine];
|
|
80
|
+
if (state.src.slice(start, start + 2) !== '$$' && (!brackets || state.src.slice(start, start + 2) !== '\\[')) return false;
|
|
81
|
+
const span = match(state, start, state.eMarks[endLine - 1]);
|
|
82
|
+
if (!span?.displayMode) return false;
|
|
83
|
+
let last = startLine;
|
|
84
|
+
while (last < endLine && state.eMarks[last] < span.end) last++;
|
|
85
|
+
if (last >= endLine || span.bodyEnd < state.bMarks[last] + state.tShift[last] ||
|
|
86
|
+
state.src.slice(span.end, state.eMarks[last]).trim()) return false;
|
|
87
|
+
if (silent) return true;
|
|
88
|
+
const parts = [];
|
|
89
|
+
for (let line = startLine; line <= last; line++) {
|
|
90
|
+
const from = line === startLine ? span.bodyStart : state.bMarks[line] + state.tShift[line];
|
|
91
|
+
const to = line === last ? span.bodyEnd : state.eMarks[line];
|
|
92
|
+
parts.push(state.src.slice(from, to));
|
|
93
|
+
}
|
|
94
|
+
const token = state.push('math_block', 'math', 0);
|
|
95
|
+
token.block = true;
|
|
96
|
+
token.markup = span.open;
|
|
97
|
+
token.content = parts.join('\n');
|
|
98
|
+
token.meta = {mathSource: span.open + token.content + span.close};
|
|
99
|
+
token.map = [startLine, last + 1];
|
|
100
|
+
state.line = last + 1;
|
|
101
|
+
return true;
|
|
102
|
+
}, {alt: ['paragraph', 'reference', 'blockquote', 'list']});
|
|
103
|
+
}
|
|
@@ -5,7 +5,7 @@ export function separate(c, endA = c.before.length, endB = c.after.length) {
|
|
|
5
5
|
const {before, after, safe, raw, spend} = c;
|
|
6
6
|
if (!raw) for (let i = c.result.length - 1; i > 0; i--) {
|
|
7
7
|
const p = c.result[i - 1], q = c.result[i], side = p[0][0] === p[0][1] ? 1 : p[1][0] === p[1][1] ? 0 : -1, text = side ? after : before;
|
|
8
|
-
if (side >= 0 && !/\S/.test(text.slice(...p[side]))) {
|
|
8
|
+
if (!c.evidence && side >= 0 && !/\S/.test(text.slice(...p[side]))) {
|
|
9
9
|
let n = 0;
|
|
10
10
|
while (p[0][1] + n < q[0][0] && text[p[side][0] + n] === text[p[side][1] + n] && spend()) n++;
|
|
11
11
|
if (p[0][1] + n === q[0][0] && p.slice(0, 2).every((span, k) => safe(k ? after : before, span[0] + n) && safe(k ? after : before, span[1] + n)))
|
|
@@ -20,7 +20,7 @@ export function separate(c, endA = c.before.length, endB = c.after.length) {
|
|
|
20
20
|
keep[3][0] = x[0] === x[1] ? 2 : y[0] === y[1] ? 0 : 1;
|
|
21
21
|
c.result.splice(i - 1, 2, [x, y, false, keep[3]]);
|
|
22
22
|
}
|
|
23
|
-
if (!raw) for (let i = 0; i < c.result.length; i++) {
|
|
23
|
+
if (!raw && !c.evidence) for (let i = 0; i < c.result.length; i++) {
|
|
24
24
|
const p = c.result[i], q = c.result[i + 1], side = p[0][0] === p[0][1] ? 1 : p[1][0] === p[1][1] ? 0 : -1;
|
|
25
25
|
if (side < 0 || p[2]) continue;
|
|
26
26
|
const text = side ? after : before;
|