rapier-markdown-kit 1.2.11 → 1.2.12
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/dist/agent/vendor/hyphen-en-us/SOURCE.json +3 -3
- package/dist/agent/vendor/hyphen-en-us/patterns.js +4 -3
- package/dist/kit/export-fonts.mjs +7 -0
- package/dist/kit/ledger/authorship.mjs +10 -19
- package/dist/kit/ledger/format.mjs +1 -1
- package/dist/kit/ledger/history.mjs +12 -3
- package/dist/kit/ledger/inverse-source.mjs +9 -126
- package/dist/kit/ledger/journal-records.mjs +2 -17
- package/dist/kit/ledger/source.mjs +323 -0
- package/dist/kit/pdf-glyphs.mjs +388 -96
- package/dist/kit/pdf-vectors.mjs +7 -6
- package/dist/kit/render-markdown.mjs +9 -6
- package/dist/kit/render-print.mjs +193 -51
- package/dist/kit/render-sanitize.mjs +7 -7
- package/dist/kit/render.mjs +16 -1
- package/dist/layout/preparation.mjs +1 -1
- package/dist/spec/md-assets.mjs +99 -8
- package/package.json +1 -1
- package/style.css +33 -7
package/dist/spec/md-assets.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
// Pure text and markdown-it tokens: no DOM, no byte decoding, no globalThis.Rapier* (MIT kit; decoders stay in images/assets.mjs).
|
|
3
3
|
import {isWordSidecarBlock} from './word-sidecar.mjs';
|
|
4
|
+
import {sourceFacts, sourceView, sourceEdit} from '../kit/ledger/source.mjs';
|
|
4
5
|
|
|
5
6
|
export const IMAGE_LIMITS = Object.freeze({bytes: 16 * 1024 * 1024, sourceChars: 64 * 1024 * 1024, dimension: 16384, pixels: 24000000, assets: 1024});
|
|
6
7
|
const parsers = new WeakMap();
|
|
@@ -513,17 +514,107 @@ export function retireDeletedImageDefinitions(before, after, splices, factory) {
|
|
|
513
514
|
} catch (_) { return []; }
|
|
514
515
|
}
|
|
515
516
|
|
|
517
|
+
const omissionCache = new WeakMap();
|
|
518
|
+
function payloadFacts(text) {
|
|
519
|
+
let bad = 0, equals = 0;
|
|
520
|
+
for (let at = 0; at < text.length; at++) {
|
|
521
|
+
const code = text.charCodeAt(at);
|
|
522
|
+
if (code === 61) equals++;
|
|
523
|
+
else if (!(code >= 65 && code <= 90 || code >= 97 && code <= 122 || code >= 48 && code <= 57 || code === 43 || code === 47)) bad++;
|
|
524
|
+
}
|
|
525
|
+
return {bad, equals};
|
|
526
|
+
}
|
|
527
|
+
function payloadBytes(length, bad, equals, tail) {
|
|
528
|
+
return length % 4 === 0 && !bad && equals <= 2 && length > equals && tail === '='.repeat(equals)
|
|
529
|
+
? length / 4 * 3 - equals : null;
|
|
530
|
+
}
|
|
531
|
+
function previousOmissions(view, kind) {
|
|
532
|
+
const changes = [];
|
|
533
|
+
let cursor = view, held;
|
|
534
|
+
while (!(held = omissionCache.get(cursor)?.[kind])) {
|
|
535
|
+
const after = cursor, change = sourceEdit(cursor);
|
|
536
|
+
if (!change || changes.length === 64 || !(cursor = change.before.deref())) return new Map();
|
|
537
|
+
changes.push({...change, after});
|
|
538
|
+
}
|
|
539
|
+
let rows = [...held.values()];
|
|
540
|
+
for (const change of changes.reverse()) {
|
|
541
|
+
const end = change.pos + change.removed, delta = change.inserted - change.removed;
|
|
542
|
+
rows = rows.flatMap(row => {
|
|
543
|
+
const permitted = kind === 'asset' ? /^[A-Za-z0-9+/=\\;&%#._~-]*$/
|
|
544
|
+
: row.profile === 'base64' ? /^[A-Za-z0-9+/=\s]*$/ : /^[A-Za-z0-9\-._~!$&*+;=:@/?%]*$/;
|
|
545
|
+
if (change.pos >= row.payloadStart && end <= row.end && permitted.test(change.text)) {
|
|
546
|
+
const next = {...row, end: row.end + delta, chars: row.chars + delta};
|
|
547
|
+
if (kind === 'asset') {
|
|
548
|
+
const was = payloadFacts(change.before.deref().slice(change.pos, end)), now = payloadFacts(change.text);
|
|
549
|
+
next.bad += now.bad - was.bad; next.equals += now.equals - was.equals;
|
|
550
|
+
next.bytes = payloadBytes(next.end - next.payloadStart, next.bad, next.equals,
|
|
551
|
+
next.equals <= 2 ? change.after.slice(next.end - next.equals, next.end) : '');
|
|
552
|
+
}
|
|
553
|
+
return [next];
|
|
554
|
+
}
|
|
555
|
+
if (change.pos > row.end) return [row];
|
|
556
|
+
if (end < row.start) return [{...row, start: row.start + delta, end: row.end + delta,
|
|
557
|
+
...(row.payloadStart === undefined ? {} : {payloadStart: row.payloadStart + delta})}];
|
|
558
|
+
return [];
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
return new Map(rows.map(row => [row.start, row]));
|
|
562
|
+
}
|
|
563
|
+
function dataMatches(source, kind, envelopes = []) {
|
|
564
|
+
const measured = sourceFacts(source), view = sourceView(source), cached = omissionCache.get(view);
|
|
565
|
+
if (cached?.[kind]) return cached[kind];
|
|
566
|
+
const prior = previousOmissions(view, kind), rows = new Map();
|
|
567
|
+
const pattern = kind === 'asset' ? /data:(image\/[a-z0-9.+-]+);base64,[A-Za-z0-9+/=\\;&%#._~-]*/iy
|
|
568
|
+
: /data:([a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+)(?:;[^,\s"'<>)]*)?,/iy;
|
|
569
|
+
let end = 0, envelopeIndex = 0;
|
|
570
|
+
for (const at of measured.markers) {
|
|
571
|
+
if (kind === 'asset' && at < end) continue;
|
|
572
|
+
while (envelopeIndex < envelopes.length && envelopes[envelopeIndex].end <= at) envelopeIndex++;
|
|
573
|
+
if (envelopes[envelopeIndex]?.start <= at) continue;
|
|
574
|
+
let row = prior.get(at);
|
|
575
|
+
if (!row) {
|
|
576
|
+
pattern.lastIndex = at;
|
|
577
|
+
const match = pattern.exec(source);
|
|
578
|
+
if (!match) continue;
|
|
579
|
+
if (kind === 'asset') {
|
|
580
|
+
const value = match[0], payload = value.slice(value.indexOf(',') + 1);
|
|
581
|
+
const {bad, equals} = payloadFacts(payload);
|
|
582
|
+
const bytes = payloadBytes(payload.length, bad, equals, equals <= 2 ? payload.slice(payload.length - equals) : '');
|
|
583
|
+
row = {start: at, end: pattern.lastIndex, chars: value.length,
|
|
584
|
+
domain: 'image_bytes', reason: 'embedded_asset', profile: 'embedded', type: match[1].toLowerCase(), bytes,
|
|
585
|
+
payloadStart: pattern.lastIndex - payload.length, bad, equals};
|
|
586
|
+
} else {
|
|
587
|
+
const payloadStart = pattern.lastIndex, base64 = /;base64,/i.test(match[0]);
|
|
588
|
+
let to = payloadStart;
|
|
589
|
+
const permitted = base64 ? /[A-Za-z0-9+/=\s]/ : /[A-Za-z0-9\-._~!$&*+;=:@/?%]/;
|
|
590
|
+
while (to < source.length && permitted.test(source[to])) to++;
|
|
591
|
+
row = {start: at, end: to, payloadStart, type: match[1], chars: to - at, profile: base64 ? 'base64' : 'data'};
|
|
592
|
+
}
|
|
593
|
+
Object.freeze(row);
|
|
594
|
+
}
|
|
595
|
+
rows.set(at, row); end = row.end;
|
|
596
|
+
}
|
|
597
|
+
omissionCache.set(view, {...cached, [kind]: rows});
|
|
598
|
+
return rows;
|
|
599
|
+
}
|
|
516
600
|
export function assetOmissions(source) {
|
|
517
601
|
if (typeof source !== 'string') return [];
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
602
|
+
return [...dataMatches(source, 'asset').values()].map(({payloadStart, bad, equals, ...row}) => row);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
export function dataOmissions(source) {
|
|
606
|
+
const envelopes = assetOmissions(source), spans = [...envelopes];
|
|
607
|
+
let envelopeIndex = 0, end = 0;
|
|
608
|
+
for (const row of dataMatches(source, 'generic', envelopes).values()) {
|
|
609
|
+
if (row.start < end) continue;
|
|
610
|
+
while (envelopeIndex < envelopes.length && envelopes[envelopeIndex].end <= row.start) envelopeIndex++;
|
|
611
|
+
const envelope = envelopes[envelopeIndex];
|
|
612
|
+
if (envelope && envelope.start <= row.start) { end = envelope.end; continue; }
|
|
613
|
+
const {payloadStart, ...span} = row;
|
|
614
|
+
if (row.end > payloadStart) spans.push(span);
|
|
615
|
+
end = row.end;
|
|
525
616
|
}
|
|
526
|
-
return spans;
|
|
617
|
+
return spans.sort((a, b) => a.start - b.start);
|
|
527
618
|
}
|
|
528
619
|
export function isAssetBlock(raw) {
|
|
529
620
|
if (isWordSidecarBlock(raw)) return true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rapier-markdown-kit",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.12",
|
|
4
4
|
"description": "Read, render and write Self-contained Markdown: one .md file that carries its pictures, drawings and layout, and that any Markdown app can read. HTML renderer, stylesheet, text layout, Will/1 agent permissions, and an edit history with authorship and offline merge. MIT, no dependencies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
package/style.css
CHANGED
|
@@ -219,6 +219,30 @@
|
|
|
219
219
|
at admission; only renderer paint omissions are corrected here. */
|
|
220
220
|
:where(.md-render) svg.rapier-native-flowchart{display:block;margin-inline:auto;height:auto}
|
|
221
221
|
:where(.md-render) svg.rapier-diagram{display:block;margin-inline:auto;height:auto;text-rendering:optimizeLegibility}
|
|
222
|
+
/* The same token bridge serves the document, live preview and portable page. */
|
|
223
|
+
:where(.md-render) :is(.diagram-block,.diagram-live-preview){
|
|
224
|
+
--pele-bg:transparent;
|
|
225
|
+
--pele-fg:var(--color-text,var(--md-color-text));
|
|
226
|
+
--pele-line:var(--color-text,var(--md-color-text));
|
|
227
|
+
--pele-muted:var(--color-text-secondary,var(--md-color-text-secondary));
|
|
228
|
+
--pele-surface:var(--color-surface-2,var(--md-color-surface-2));
|
|
229
|
+
--pele-surface-alt:var(--color-surface-hover,var(--md-color-border-focus));
|
|
230
|
+
--pele-border:var(--color-border,var(--color-surface-hover,var(--md-color-border)));
|
|
231
|
+
--pele-accent:var(--color-accent,var(--md-color-accent));
|
|
232
|
+
--pele-font:var(--font-sans,var(--md-font-sans));
|
|
233
|
+
--pele-font-mono:var(--font-mono,var(--md-font-mono));
|
|
234
|
+
--pele-radius:0;
|
|
235
|
+
--pele-heading-weight:var(--md-fw-semibold);
|
|
236
|
+
--pele-title-weight:var(--md-fw-semibold);
|
|
237
|
+
--pele-series-1:var(--pele-accent);
|
|
238
|
+
--pele-series-2:#4f91f7;
|
|
239
|
+
--pele-series-3:#35c57a;
|
|
240
|
+
--pele-series-4:#e6b91e;
|
|
241
|
+
--pele-series-5:#9b6dea;
|
|
242
|
+
--pele-series-6:#f05a62;
|
|
243
|
+
--pele-series-7:color-mix(in srgb,var(--pele-accent) 50%,#4f91f7);
|
|
244
|
+
--pele-series-8:color-mix(in srgb,#9b6dea 50%,#f05a62);
|
|
245
|
+
}
|
|
222
246
|
|
|
223
247
|
/* Radar gives these SVG labels a color declaration; SVG text needs a fill. */
|
|
224
248
|
:where(.md-render) svg.rapier-diagram[aria-roledescription="radar"] :is(.radarTitle,.radarAxisLabel){fill:currentColor}
|
|
@@ -426,11 +450,11 @@
|
|
|
426
450
|
/* The house focus (rapier-app.css) in the document and on the exported page: the underline thickens, no ring. */
|
|
427
451
|
.md-render a:focus-visible,.md-render ul.rapier-jump-list>li>a:focus-visible,.md-render .footnote-ref a:focus-visible,.md-render summary:focus-visible{outline:none;text-decoration:underline;text-decoration-thickness:3px;text-underline-offset:.2em}
|
|
428
452
|
|
|
453
|
+
/* The title over a table of contents ("Contents", "Table of contents", "Jump to"), as written and bigger than its
|
|
454
|
+
entries. */
|
|
429
455
|
.md-render .rapier-jump-label{
|
|
430
456
|
margin:0 0 calc(.5 * var(--md-unit));
|
|
431
|
-
font:var(--md-fw-semibold) calc(.
|
|
432
|
-
letter-spacing:var(--md-track-caps);
|
|
433
|
-
text-transform:uppercase;
|
|
457
|
+
font:var(--md-fw-semibold) calc(1.3 * var(--md-unit))/1.3 var(--md-font-sans);
|
|
434
458
|
color:var(--md-color-text);
|
|
435
459
|
}
|
|
436
460
|
.md-render .rapier-jump-label strong{font:inherit}
|
|
@@ -459,20 +483,22 @@
|
|
|
459
483
|
top:var(--md-jump-circle-top);
|
|
460
484
|
width:var(--md-jump-circle);height:var(--md-jump-circle);
|
|
461
485
|
box-sizing:border-box;
|
|
462
|
-
border:2px solid var(--md-color-
|
|
486
|
+
border:2px solid var(--md-color-line);
|
|
463
487
|
background:var(--md-color-bg);
|
|
464
488
|
border-radius:50%;
|
|
465
489
|
z-index:1;
|
|
466
490
|
}
|
|
467
|
-
/* The
|
|
468
|
-
|
|
491
|
+
/* The circles and dots wear the text's black or white; only the underline is accent. Three
|
|
492
|
+
3px dots, evenly spaced, from below this circle to the next circle's top; the line runs on under that circle's
|
|
493
|
+
center, which covers it. None after the last item. --md-item-gap is the gap the spacing settings give. */
|
|
469
494
|
.md-render ul.rapier-jump-list>li:not(:last-child)::after{
|
|
470
495
|
content:'';
|
|
471
496
|
position:absolute;
|
|
472
497
|
inset-inline-start:calc(var(--md-jump-marker-inline-start) + var(--md-jump-circle) / 2 - 1.5px);
|
|
473
498
|
top:calc(var(--md-jump-circle-top) + var(--md-jump-circle));
|
|
474
499
|
bottom:calc(-1 * (var(--md-item-gap, var(--md-jump-gap)) + var(--md-jump-circle-top) + var(--md-jump-circle) / 2));
|
|
475
|
-
|
|
500
|
+
width:3px;
|
|
501
|
+
background:radial-gradient(circle closest-side,var(--md-color-line) 92%,transparent) top/3px calc((100% - var(--md-jump-circle) / 2) / 3) repeat-y;
|
|
476
502
|
z-index:0;
|
|
477
503
|
}
|
|
478
504
|
.md-render ul.rapier-jump-list>li>a{
|