rapier-markdown-kit 1.1.104 → 1.2.5
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 +8 -4
- package/README.md +50 -7
- package/dist/images/gif.mjs +49 -0
- package/dist/images/playback.mjs +103 -0
- package/dist/kit/conformance/03-wrap-around-silhouette.md +1 -1
- package/dist/kit/conformance/04-wrap-box.md +1 -1
- package/dist/kit/conformance/05-behind.md +1 -1
- package/dist/kit/conformance/06-front.md +1 -1
- package/dist/kit/conformance/07-rotate-raster.md +1 -1
- package/dist/kit/conformance/08-drawing-rotation.md +1 -1
- package/dist/kit/conformance/09-drawing-ring-interior.expected.json +18 -18
- package/dist/kit/conformance/09-drawing-ring-interior.md +1 -1
- package/dist/kit/conformance/10-both-sides.md +1 -1
- package/dist/kit/conformance/11-neighbour-skips-image.md +1 -1
- package/dist/kit/conformance/{12-heading-barrier.expected.json → 12-rule-barrier.expected.json} +1 -1
- package/dist/kit/conformance/{12-heading-barrier.md → 12-rule-barrier.md} +2 -2
- package/dist/kit/conformance/README.md +19 -28
- package/dist/kit/conformance/run.mjs +10 -8
- package/dist/kit/ledger/authored-origin.mjs +49 -0
- package/dist/kit/ledger/authorship.mjs +18 -9
- package/dist/kit/ledger/carried.mjs +10 -5
- package/dist/kit/ledger/data.mjs +7 -4
- package/dist/kit/ledger/format.mjs +37 -11
- package/dist/kit/ledger/hash.mjs +31 -15
- package/dist/kit/ledger/history.mjs +48 -10
- package/dist/kit/ledger/inverse-source.mjs +111 -49
- package/dist/kit/ledger/journal-records.mjs +24 -29
- package/dist/kit/ledger/merge.mjs +51 -15
- package/dist/kit/ledger/text-merge.mjs +75 -29
- package/dist/kit/ledger/transport.mjs +30 -45
- package/dist/kit/pdf-vectors.mjs +6 -2
- package/dist/kit/print-capitals.mjs +0 -1
- package/dist/kit/rapier-ledger.mjs +4 -2
- package/dist/kit/render-ink.mjs +1 -1
- package/dist/kit/render-markdown.mjs +155 -31
- package/dist/kit/render-print.mjs +173 -61
- package/dist/kit/render-sanitize.mjs +25 -15
- package/dist/kit/render-styles.mjs +3 -3
- package/dist/kit/render.mjs +37 -115
- package/dist/layout/model.mjs +10 -8
- package/dist/layout/paragraph-fit.mjs +1 -15
- package/dist/layout/typography.mjs +2 -1
- package/dist/packages/rapier-diff/concerns.mjs +163 -0
- package/dist/packages/rapier-diff/diff.mjs +68 -0
- package/dist/packages/rapier-diff/index.mjs +4 -701
- package/dist/packages/rapier-diff/matching.mjs +157 -0
- package/dist/packages/rapier-diff/merge.mjs +226 -0
- package/dist/packages/rapier-diff/moves.mjs +130 -0
- package/dist/packages/rapier-diff/structure.mjs +50 -0
- package/dist/packages/rapier-diff/tokens.mjs +72 -0
- package/dist/packages/rapier-diff/walk.mjs +219 -0
- package/dist/spec/document-fonts.mjs +1 -1
- package/dist/spec/document-settings.mjs +11 -164
- package/dist/spec/html-blocks.mjs +618 -79
- package/dist/spec/md-assets.mjs +29 -6
- package/dist/spec/md-layout.mjs +9 -1
- package/dist/spec/md-marks.mjs +25 -147
- package/dist/spec/md-source.mjs +69 -26
- package/package.json +1 -1
- package/style.css +7 -51
package/dist/spec/md-assets.mjs
CHANGED
|
@@ -22,7 +22,7 @@ export function installMarkdownImages(md) {
|
|
|
22
22
|
const normalize = md.normalizeLink;
|
|
23
23
|
// An exact base64 data URL contains no URI escapes or host name to normalize.
|
|
24
24
|
// Preserve its spelling and avoid allocating one URI-normalizer item per raster byte.
|
|
25
|
-
md.normalizeLink = url => /^data:image\/(?:jxl|png|jpeg|webp|svg\+xml);base64,[A-Za-z0-9+/]+={0,2}$/i.test(url) ? url : normalize(url);
|
|
25
|
+
md.normalizeLink = url => /^data:image\/(?:jxl|png|jpeg|webp|gif|svg\+xml);base64,[A-Za-z0-9+/]+={0,2}$/i.test(url) ? url : normalize(url);
|
|
26
26
|
const reference = md.block.ruler.__rules__.find(row => row.name === 'reference').fn;
|
|
27
27
|
md.block.ruler.at('reference', (state, start, end, silent) => {
|
|
28
28
|
if (silent) return reference(state, start, end, silent);
|
|
@@ -199,7 +199,7 @@ export function dataImage(url) {
|
|
|
199
199
|
if (typeof url !== 'string' || url.length > IMAGE_LIMITS.sourceChars) return null;
|
|
200
200
|
const prepared = preparedDefinitions.find(row => row.image.url === url)?.image;
|
|
201
201
|
if (prepared) return {codec: prepared.codec, byteLength: prepared.byteLength, payloadStart: url.indexOf(',') + 1};
|
|
202
|
-
const match = /^data:(image\/(?:jxl|png|jpeg|webp|svg\+xml));base64,([A-Za-z0-9+/]+={0,2})$/i.exec(url);
|
|
202
|
+
const match = /^data:(image\/(?:jxl|png|jpeg|webp|gif|svg\+xml));base64,([A-Za-z0-9+/]+={0,2})$/i.exec(url);
|
|
203
203
|
if (!match || match[2].length % 4) return null;
|
|
204
204
|
const padding = match[2].endsWith('==') ? 2 : match[2].endsWith('=') ? 1 : 0;
|
|
205
205
|
const byteLength = match[2].length / 4 * 3 - padding;
|
|
@@ -451,7 +451,7 @@ function occurrenceChange(image, splices) {
|
|
|
451
451
|
|
|
452
452
|
// A permissive gate keeps ordinary typing out of full-document image parsing.
|
|
453
453
|
// Recognition and retirement proof still belong to Markdown-it below.
|
|
454
|
-
export function mayRetireImageDefinitions(source, splices, factory) {
|
|
454
|
+
export function mayRetireImageDefinitions(source, splices, factory, hint) {
|
|
455
455
|
const store = typeof source === 'string' ? null : source;
|
|
456
456
|
// A definition's closing bracket is immediately followed by its colon (parseAssets uses the same necessary condition).
|
|
457
457
|
if (store ? !store.contains(']:') || !store.contains('data:image/', true)
|
|
@@ -468,13 +468,36 @@ export function mayRetireImageDefinitions(source, splices, factory) {
|
|
|
468
468
|
if (!row.removed && !row.inserted) return false;
|
|
469
469
|
const start = original(row.pos, false, index), end = original(row.pos + row.removed.length, true, index);
|
|
470
470
|
if (store) {
|
|
471
|
+
// No image use exists without this marker. A definition appendix alone must
|
|
472
|
+
// not make a prose keystroke join the document to prove that.
|
|
473
|
+
if (!store.contains('![')) return false;
|
|
471
474
|
const from = Math.max(0, start - 8192);
|
|
472
475
|
const local = store.readSlice(from, Math.min(store.length, end + 4));
|
|
473
476
|
const image = local.lastIndexOf('![', end - from);
|
|
474
477
|
const separator = Math.max(local.lastIndexOf('\n\n', start - from),
|
|
475
478
|
local.lastIndexOf('\r\n\r\n', start - from), local.lastIndexOf('\r\r', start - from));
|
|
476
479
|
if (image >= 0) return image + from >= start || separator < image;
|
|
477
|
-
|
|
480
|
+
const exclude = left => {
|
|
481
|
+
if (hint && splices.length === 1) hint.excluded = {start: left, end};
|
|
482
|
+
return false;
|
|
483
|
+
};
|
|
484
|
+
if (separator >= 0 || from === 0) return exclude(separator >= 0 ? from + separator + 4 : 0);
|
|
485
|
+
// Still inside a longer paragraph. Walk back in slices to the blank line.
|
|
486
|
+
// Finding an image there is the same "possible" answer as the 8KiB window;
|
|
487
|
+
// finding the break, or none, does not read the joined document.
|
|
488
|
+
let cursor = from;
|
|
489
|
+
while (cursor > 0) {
|
|
490
|
+
const sliceFrom = Math.max(0, cursor - 8192);
|
|
491
|
+
const chunk = store.readSlice(sliceFrom, Math.min(store.length, cursor + 3));
|
|
492
|
+
const limit = cursor - sliceFrom;
|
|
493
|
+
const sep = Math.max(chunk.lastIndexOf('\n\n', limit), chunk.lastIndexOf('\r\n\r\n', limit),
|
|
494
|
+
chunk.lastIndexOf('\r\r', limit));
|
|
495
|
+
const imageAt = chunk.lastIndexOf('![', limit);
|
|
496
|
+
if (imageAt >= 0 && (sep < 0 || imageAt > sep)) return true;
|
|
497
|
+
if (sep >= 0 || sliceFrom === 0) return exclude(sep >= 0 ? sliceFrom + sep + 4 : 0);
|
|
498
|
+
cursor = sliceFrom;
|
|
499
|
+
}
|
|
500
|
+
return false;
|
|
478
501
|
}
|
|
479
502
|
const image = source.lastIndexOf('![', end);
|
|
480
503
|
return image >= 0 && (image >= start || Math.max(source.lastIndexOf('\n\n', start),
|
|
@@ -562,10 +585,10 @@ export function serializeAsset(asset) {
|
|
|
562
585
|
}
|
|
563
586
|
// Pure: re-derived at commit against the moved document; appends only at the text's end.
|
|
564
587
|
// A replay supplies the retained definition's line ending instead of choosing one from later prose.
|
|
565
|
-
export function appendAssetText(source, asset, lineEnding) {
|
|
588
|
+
export function appendAssetText(source, asset, lineEnding, {preserveLabel = false} = {}) {
|
|
566
589
|
if (typeof source !== 'string' || !asset || normalizeLabel(asset.label) !== asset.id) return fail('image_source_invalid');
|
|
567
590
|
const parsed = documentAssets(source), title = assetTitle(asset.title);
|
|
568
|
-
const existing = [...parsed.assets.values()].find(row => row.url === asset.url && row.title === title);
|
|
591
|
+
const existing = !preserveLabel && [...parsed.assets.values()].find(row => row.url === asset.url && row.title === title);
|
|
569
592
|
if (existing) return {source, id: existing.id, reference: existing.label, added: false, suffix: ''};
|
|
570
593
|
const previous = parsed.references[asset.id];
|
|
571
594
|
const block = serializeAsset(asset);
|
package/dist/spec/md-layout.mjs
CHANGED
|
@@ -152,6 +152,14 @@ export function imageStyle(value) {
|
|
|
152
152
|
|
|
153
153
|
export const linesHeightCss = lines => lines === 1 ? '1cap' : 'calc(' + (lines - 1) + ' * var(--md-line, 1.6em) + 1cap)';
|
|
154
154
|
|
|
155
|
+
// An HTML object has an outer clip box and an independent browser viewport.
|
|
156
|
+
// SCALE keeps that authored viewport; REFLOW keeps measured content height.
|
|
157
|
+
export function objectHeight(width, naturalHeight, attrs, line) {
|
|
158
|
+
if (attrs.height && attrs.height !== 'auto') return Math.min(4096, Number(attrs.height) * line);
|
|
159
|
+
const [w, h] = (attrs.viewport || '640x360').split('x').map(Number);
|
|
160
|
+
return attrs.resize === 'scale' ? Math.min(4096, width * h / w) : naturalHeight;
|
|
161
|
+
}
|
|
162
|
+
|
|
155
163
|
// Controls remain real DOM nodes while their surrounding text flows. A checkbox, link or
|
|
156
164
|
// disclosure is not a reason to exclude its entire block from picture placement.
|
|
157
165
|
export function wrapTextBlock(element) {
|
|
@@ -159,7 +167,7 @@ export function wrapTextBlock(element) {
|
|
|
159
167
|
element.textContent.trim() && !element.querySelector('img,table,pre,figure,math,.math-rendered') ? element : null;
|
|
160
168
|
}
|
|
161
169
|
|
|
162
|
-
// The one wrap-owner rule (layout/browser.js,
|
|
170
|
+
// The one wrap-owner rule (layout/browser.js, kit/render.mjs, editor/share.js): text after, else before, skipping pictures,
|
|
163
171
|
// metadata and empty paragraphs; any other block is a barrier in that direction.
|
|
164
172
|
export function wrapNeighbour(node, classify) {
|
|
165
173
|
for (const step of [n => n && n.nextElementSibling, n => n && n.previousElementSibling]) {
|
package/dist/spec/md-marks.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// The one owner of inline typography,
|
|
2
|
+
// The one owner of inline typography, ink and page breaks.
|
|
3
3
|
import {markdownSourcePositions} from './md-source.mjs';
|
|
4
4
|
|
|
5
5
|
// The exported page retains this reader alone. The editor uses the same closure, then adds its writers and scans.
|
|
@@ -17,8 +17,8 @@ const inkReader = () => {
|
|
|
17
17
|
// size when the stroke was drawn and at the frame's offset from the words' box, both in hundredths of an em; w is the pen's width as Draw
|
|
18
18
|
// counts a nib (2 to 24; 9 is the default and is never written): the stroke is drawn 0.11 em wide at 9 and in proportion to w otherwise; PATH
|
|
19
19
|
// is the stroke, its first point absolute in the frame and the rest as moves, hundredths of an em. One spelling: the fields in this order,
|
|
20
|
-
// one space between, integers only. A comment that does not read this way is ordinary comment text and marks nothing. Ink pairs
|
|
21
|
-
//
|
|
20
|
+
// one space between, integers only. A comment that does not read this way is ordinary comment text and marks nothing. Ink pairs nest:
|
|
21
|
+
// each closer takes the innermost open ink opener. Arrow/end alone take a pairing number, an end takes no other data; under, strike and arrow may omit a path for a line
|
|
22
22
|
// derived from their words.
|
|
23
23
|
const INK_KINDS = Object.freeze(['under', 'strike', 'ring', 'bracket', 'free', 'arrow', 'end']);
|
|
24
24
|
|
|
@@ -29,9 +29,6 @@ const inkReader = () => {
|
|
|
29
29
|
+ '(?: (' + INK_INT + ',' + INK_INT + '(?: ' + INK_INT + ',' + INK_INT + ')*))?';
|
|
30
30
|
const INK_OPEN_PATTERN = '<!--ink ' + INK_OPEN_BODY + '-->';
|
|
31
31
|
|
|
32
|
-
// Relocated into the parse Worker by .toString() with parseInkOpen and isInkClose (engine.js _buildParseWorkerSource):
|
|
33
|
-
// reads only its parameter and the planted INK_PATH_MAX, INK_INT_MAX and TEXT_COLOR_NAMES; parseInkOpen reads
|
|
34
|
-
// INK_OPEN_PATTERN and this function; isInkClose reads INK_CLOSE.
|
|
35
32
|
function readInkMatch(match) {
|
|
36
33
|
const linked = match[1] === 'arrow' || match[1] === 'end';
|
|
37
34
|
if (linked !== (match[2] != null) || (!linked && match[1] !== 'under' && match[1] !== 'strike' && match[9] == null)) return null;
|
|
@@ -69,73 +66,20 @@ const inkReader = () => {
|
|
|
69
66
|
}
|
|
70
67
|
return { TEXT_COLOR_NAMES, COLOR_NAME_ALT, COLOR_VALUE, INK_KINDS, INK_PATH_MAX, INK_INT_MAX, INK_OPEN_PATTERN, readInkMatch, parseInkOpen, parseInkBody };
|
|
71
68
|
};
|
|
72
|
-
const { TEXT_COLOR_NAMES,
|
|
69
|
+
const { TEXT_COLOR_NAMES, COLOR_VALUE, INK_KINDS, INK_PATH_MAX, INK_INT_MAX, INK_OPEN_PATTERN, readInkMatch, parseInkOpen, parseInkBody } = inkReader();
|
|
73
70
|
export { readInkMatch, parseInkOpen, parseInkBody };
|
|
74
71
|
|
|
75
72
|
const TEXT_COLOR_HEX_TO_NAME = Object.freeze(
|
|
76
73
|
Object.fromEntries(Object.entries(TEXT_COLOR_NAMES).map(([name, hex]) => [hex, name]))
|
|
77
74
|
);
|
|
78
|
-
const COLOR_CLOSE = '<!--/c-->';
|
|
79
75
|
const PAGE_BREAK_MARKER = '<!--md-break:v1 page-->';
|
|
80
76
|
|
|
81
|
-
export { TEXT_COLOR_NAMES,
|
|
82
|
-
|
|
83
|
-
export function formatColorOpen(hex) {
|
|
84
|
-
return '<!--c ' + (TEXT_COLOR_HEX_TO_NAME[hex] || hex) + '-->';
|
|
85
|
-
}
|
|
77
|
+
export { TEXT_COLOR_NAMES, PAGE_BREAK_MARKER };
|
|
86
78
|
|
|
87
79
|
export function formatColorRun(hex, content) {
|
|
88
80
|
return typography.wrap({color: hex}, content);
|
|
89
81
|
}
|
|
90
82
|
|
|
91
|
-
// Relocated into the parse Worker by .toString() (engine.js _buildParseWorkerSource): read only parameters, TEXT_COLOR_NAMES and COLOR_CLOSE.
|
|
92
|
-
|
|
93
|
-
// Returns {hex, length in UTF-16 units} or null; not end-anchored.
|
|
94
|
-
export function matchColorOpen(text) {
|
|
95
|
-
if (typeof text !== 'string') return null;
|
|
96
|
-
const match = new RegExp('^<!--c (#[0-9a-f]{6}|' + Object.keys(TEXT_COLOR_NAMES).join('|') + ')-->').exec(text);
|
|
97
|
-
if (!match) return null;
|
|
98
|
-
const value = match[1];
|
|
99
|
-
return { hex: value.charCodeAt(0) === 0x23 /* '#' */ ? value : TEXT_COLOR_NAMES[value], length: match[0].length };
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export function parseColorOpen(comment) {
|
|
103
|
-
if (typeof comment !== 'string') return null;
|
|
104
|
-
const match = new RegExp('^<!--c (#[0-9a-f]{6}|' + Object.keys(TEXT_COLOR_NAMES).join('|') + ')-->$').exec(comment);
|
|
105
|
-
if (!match) return null;
|
|
106
|
-
const value = match[1];
|
|
107
|
-
return value.charCodeAt(0) === 0x23 /* '#' */ ? value : TEXT_COLOR_NAMES[value];
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export function isColorClose(comment) {
|
|
111
|
-
return comment === COLOR_CLOSE;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export function scanColorMarkers(source) {
|
|
115
|
-
const text = String(source == null ? '' : source);
|
|
116
|
-
const scan = new RegExp('<!--c (?:(#[0-9a-f]{6})|(' + COLOR_NAME_ALT + '))-->|<!--/c-->', 'g');
|
|
117
|
-
const markers = [];
|
|
118
|
-
let match;
|
|
119
|
-
while ((match = scan.exec(text))) {
|
|
120
|
-
const hex = match[1] || (match[2] ? TEXT_COLOR_NAMES[match[2]] : null) || null;
|
|
121
|
-
markers.push({ start: match.index, end: match.index + match[0].length, hex });
|
|
122
|
-
}
|
|
123
|
-
return markers;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export function pairColorMarkers(source) {
|
|
127
|
-
return pairMarkers(source, 'color');
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export function stripColorMarkers(text) {
|
|
131
|
-
return String(text == null ? '' : text).replace(new RegExp('<!--(?:c ' + COLOR_VALUE + '|/c)-->', 'g'), '');
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export function hasColorMarker(text) {
|
|
135
|
-
return new RegExp('<!--(?:c ' + COLOR_VALUE + '|/c)-->').test(String(text == null ? '' : text));
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export const FONT_CLOSE = '<!--/f-->';
|
|
139
83
|
export const FONT_STACKS = Object.freeze({
|
|
140
84
|
sans: Object.freeze({stack: 'sans', css: '', word: ''}),
|
|
141
85
|
serif: Object.freeze({stack: 'serif', css: "Georgia,'Palatino Linotype',Palatino,'Times New Roman',Times,serif", word: 'Georgia'}),
|
|
@@ -144,8 +88,7 @@ export const FONT_STACKS = Object.freeze({
|
|
|
144
88
|
hand: Object.freeze({stack: 'hand', css: 'cursive', word: 'Segoe Print'}),
|
|
145
89
|
});
|
|
146
90
|
|
|
147
|
-
// A family is one font-table name, never a CSS list or an HTML fragment.
|
|
148
|
-
// relocated into the parse worker with FONT_CLOSE and their original function names.
|
|
91
|
+
// A family is one font-table name, never a CSS list or an HTML fragment.
|
|
149
92
|
export function normalizeFontFamily(value) {
|
|
150
93
|
if (typeof value !== 'string') return null;
|
|
151
94
|
const family = value.trim();
|
|
@@ -159,60 +102,13 @@ export function fontFamilyCss(value) {
|
|
|
159
102
|
return generic ? generic.css || "'Geist',system-ui,sans-serif" : '"' + family.replace(/[\\"]/g, '\\$&') + '"';
|
|
160
103
|
}
|
|
161
104
|
|
|
162
|
-
export function formatFontOpen(family) {
|
|
163
|
-
const name = normalizeFontFamily(family);
|
|
164
|
-
if (!name) throw new TypeError('font: use one family name');
|
|
165
|
-
return '<!--f ' + name + '-->';
|
|
166
|
-
}
|
|
167
|
-
|
|
168
105
|
export function formatFontRun(family, content) {
|
|
169
106
|
return typography.wrap({family}, content);
|
|
170
107
|
}
|
|
171
108
|
|
|
172
|
-
export function matchFontOpen(text) {
|
|
173
|
-
if (typeof text !== 'string') return null;
|
|
174
|
-
const match = /^<!--f ([^<>\r\n]{1,256})-->/.exec(text);
|
|
175
|
-
const family = match && normalizeFontFamily(match[1]);
|
|
176
|
-
return family ? {family, length: match[0].length} : null;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
export function parseFontOpen(comment) {
|
|
180
|
-
const match = matchFontOpen(comment);
|
|
181
|
-
return match && match.length === comment.length ? match.family : null;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export function isFontClose(comment) {
|
|
185
|
-
return comment === FONT_CLOSE;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
export function scanFontMarkers(source) {
|
|
189
|
-
const text = String(source ?? ''), markers = [];
|
|
190
|
-
const scan = /<!--(?:f [^<>\r\n]{1,256}|\/f)-->/g;
|
|
191
|
-
for (const match of text.matchAll(scan)) {
|
|
192
|
-
const family = match[0] === FONT_CLOSE ? null : parseFontOpen(match[0]);
|
|
193
|
-
if (family || match[0] === FONT_CLOSE) markers.push({start: match.index, end: match.index + match[0].length, family});
|
|
194
|
-
}
|
|
195
|
-
return markers;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
export function pairFontMarkers(source, markers = null) {
|
|
199
|
-
return pairMarkers(source, 'font', markers);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export function stripFontMarkers(source) {
|
|
203
|
-
const text = String(source ?? '');
|
|
204
|
-
let result = '', at = 0;
|
|
205
|
-
for (const marker of scanFontMarkers(text)) { result += text.slice(at, marker.start); at = marker.end; }
|
|
206
|
-
return result + text.slice(at);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
export function hasFontMarker(source) {
|
|
210
|
-
return scanFontMarkers(source).length > 0;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
109
|
// One typed interpretation for source, DOM and native-file adapters. The worker carries this
|
|
214
110
|
// exact factory and its explicit grammar inputs; no DOM, CSS evaluator or resource loader.
|
|
215
|
-
export function createTypography({normalizeFontFamily, fontFamilyCss,
|
|
111
|
+
export function createTypography({normalizeFontFamily, fontFamilyCss, FONT_STACKS}) {
|
|
216
112
|
const LIMIT = 4096, DEPTH = 64;
|
|
217
113
|
const generics = {serif: 'serif', 'sans-serif': 'sans', monospace: 'mono', cursive: 'hand',
|
|
218
114
|
fantasy: 'fantasy', 'system-ui': 'system', 'ui-serif': 'ui-serif', 'ui-sans-serif': 'ui-sans-serif',
|
|
@@ -383,34 +279,23 @@ export function createTypography({normalizeFontFamily, fontFamilyCss, parseColor
|
|
|
383
279
|
const stack = []; let depth = 0, overflow = 0;
|
|
384
280
|
for (const token of block.children) {
|
|
385
281
|
if (token.type !== 'html_inline') { depth += token.nesting || 0; continue; }
|
|
386
|
-
const source = token.content,
|
|
387
|
-
|
|
388
|
-
: source === '<!--/c-->' || parseColorOpen(source) ? 'color'
|
|
389
|
-
: source === '<!--/f-->' || parseFontOpen(source) ? 'font' : '';
|
|
390
|
-
if (!kind) continue;
|
|
391
|
-
const close = htmlClose || source === '<!--/c-->' || source === '<!--/f-->';
|
|
282
|
+
const source = token.content, close = /^<\/span\s*>$/i.test(source);
|
|
283
|
+
if (!close && !/^<span(?=[\s>])/i.test(source)) continue;
|
|
392
284
|
if (overflow) { overflow += close ? -1 : 1; continue; }
|
|
393
285
|
if (!close) {
|
|
394
|
-
// Authored comment marks retain their original first-opener rule. Ordinary
|
|
395
|
-
// canonical spans nest; a repeated old opener remains an inert stray.
|
|
396
|
-
if (kind !== 'span' && stack.some(held => held.kind === kind)) continue;
|
|
397
286
|
if (stack.length === DEPTH) { overflow = 1; continue; }
|
|
398
|
-
const facts =
|
|
399
|
-
|
|
400
|
-
stack.push({token, kind, facts, depth, source}); continue;
|
|
287
|
+
const facts = parseOpen(source, value => md.utils.unescapeAll(value.replace(/\\/g, '\\\\')));
|
|
288
|
+
stack.push({token, facts, depth, source}); continue;
|
|
401
289
|
}
|
|
402
|
-
const held = stack.
|
|
403
|
-
if (!held || held.
|
|
404
|
-
|
|
405
|
-
if (!held.facts || depth !== held.depth) continue;
|
|
406
|
-
const type = kind === 'span' ? 'typography' : kind, opener = held.token;
|
|
290
|
+
const held = stack.pop();
|
|
291
|
+
if (!held || !held.facts || depth !== held.depth) continue;
|
|
292
|
+
const type = 'typography', opener = held.token;
|
|
407
293
|
opener.type = 'rapier_' + type + '_open'; opener.tag = 'span'; opener.nesting = 1; opener.content = '';
|
|
408
294
|
(opener.meta ||= {}).typography = Object.freeze({style: held.facts, open: held.source, close: source});
|
|
409
295
|
opener.attrSet('style', style(held.facts));
|
|
410
296
|
if (held.facts.color) opener.attrSet('data-md-color', held.facts.color);
|
|
411
297
|
if (held.facts.size) opener.attrSet('data-md-size', held.facts.size);
|
|
412
298
|
if (held.facts.family) opener.attrSet('data-md-font', held.facts.family.name);
|
|
413
|
-
if (kind === 'font') { opener.attrSet('data-md-font-open', encodeURIComponent(held.source)); opener.meta.mdFont = {source: held.source, family: held.facts.family.name}; }
|
|
414
299
|
token.type = 'rapier_' + type + '_close'; token.tag = 'span'; token.nesting = -1; token.content = '';
|
|
415
300
|
(token.meta ||= {}).typography = opener.meta.typography;
|
|
416
301
|
}
|
|
@@ -421,7 +306,7 @@ export function createTypography({normalizeFontFamily, fontFamilyCss, parseColor
|
|
|
421
306
|
return Object.freeze({size, color, family, familyCss, normalize, parseStyle, style, open, wrap, parseOpen, nodeStyle, runs, install});
|
|
422
307
|
}
|
|
423
308
|
|
|
424
|
-
export const typography = createTypography({normalizeFontFamily, fontFamilyCss,
|
|
309
|
+
export const typography = createTypography({normalizeFontFamily, fontFamilyCss, FONT_STACKS});
|
|
425
310
|
export function typographySourceRanges(source, parser) { return markdownSourcePositions(source, parser).typography; }
|
|
426
311
|
|
|
427
312
|
const INK_CLOSE = '<!--/ink-->';
|
|
@@ -485,7 +370,7 @@ export function matchInkOpen(text) {
|
|
|
485
370
|
// The opener's body is what a rendered span carries (a renderer's sanitizer drops an attribute whose value holds the
|
|
486
371
|
// comment's own close): the opener after `<!--ink ` and before `-->`, so the attribute reads `under red box=… 0,95 …`,
|
|
487
372
|
// and only a body that reads as a mark goes back. Read and write through the same grammar, so an invalid body cannot
|
|
488
|
-
// invent a mark.
|
|
373
|
+
// invent a mark.
|
|
489
374
|
export function inkOpenBody(opener) {
|
|
490
375
|
if (typeof opener !== 'string') return null;
|
|
491
376
|
const match = new RegExp('^' + INK_OPEN_PATTERN + '$').exec(opener);
|
|
@@ -557,7 +442,7 @@ export function pairInkSpans(source, markers = null) {
|
|
|
557
442
|
return pairSpanMarkers(source, 'ink', markers);
|
|
558
443
|
}
|
|
559
444
|
|
|
560
|
-
// Colour
|
|
445
|
+
// Colour keeps its first opener; ink pairs each closer with the innermost opener. The shared range
|
|
561
446
|
// shape lets custody owners keep every independent stroke, including strokes on identical words.
|
|
562
447
|
export function pairMarkers(source, kind, markers = null) {
|
|
563
448
|
const paired = pairSpanMarkers(source, kind, markers);
|
|
@@ -585,8 +470,8 @@ export function pairMarkers(source, kind, markers = null) {
|
|
|
585
470
|
return { runs, strays, arrows };
|
|
586
471
|
}
|
|
587
472
|
|
|
588
|
-
// Where a Will marker may stand (docs/will.md, Conversion). A pair inside a fence, a raw block
|
|
589
|
-
// (
|
|
473
|
+
// Where a Will marker may stand (docs/will.md, Conversion). A pair inside a fence, a raw HTML block
|
|
474
|
+
// (until its blank line) or the opening front matter is text: the line is quoted,
|
|
590
475
|
// and a Word or PDF writer that met it as a marker would govern the wrong region. The same frame
|
|
591
476
|
// as spec/frontmatter.mjs, copied here so this grammar stays inside the MIT kit's closure.
|
|
592
477
|
const WILL_BLOCK_TAGS = new Set('address article aside base basefont blockquote body caption center col colgroup dd details dialog dir div dl dt fieldset figcaption figure footer form frame frameset h1 h2 h3 h4 h5 h6 head header hr html iframe legend li link main menu menuitem nav noframes ol optgroup option p param search section summary table tbody td tfoot th thead title tr track ul'.split(' '));
|
|
@@ -724,7 +609,7 @@ function willQuotedWhere(stack) {
|
|
|
724
609
|
if (item.tags.includes('pre')) return 'raw-pre';
|
|
725
610
|
if (item.tags.includes('td') || item.tags.includes('th')) return 'raw-table';
|
|
726
611
|
if (item.tags.includes('div')) return 'raw-div';
|
|
727
|
-
return
|
|
612
|
+
return 'raw-html';
|
|
728
613
|
}
|
|
729
614
|
}
|
|
730
615
|
return null;
|
|
@@ -737,7 +622,7 @@ function willLeaf(stack) {
|
|
|
737
622
|
|
|
738
623
|
// `spans` are [start, end) of marker lines in `source` (the Will reader's own offsets).
|
|
739
624
|
// Returns the first quoted span, {start, end, where}, or null when every span may stand.
|
|
740
|
-
// `where` is `fence`, `raw-pre`, `raw-div`, `raw-table` or `frontmatter`.
|
|
625
|
+
// `where` is `fence`, `raw-pre`, `raw-div`, `raw-table`, `raw-html` (any other raw HTML block) or `frontmatter`.
|
|
741
626
|
export function quotedWillSpan(source, spans) {
|
|
742
627
|
const text = String(source ?? '');
|
|
743
628
|
const marks = Array.isArray(spans) ? spans : [];
|
|
@@ -804,19 +689,13 @@ export function quotedWillSpan(source, spans) {
|
|
|
804
689
|
}
|
|
805
690
|
|
|
806
691
|
function pairSpanMarkers(source, kind, markers = null) {
|
|
807
|
-
if (kind !== 'ink'
|
|
808
|
-
const color = kind === 'color', font = kind === 'font';
|
|
692
|
+
if (kind !== 'ink') throw new TypeError('Unknown paired marker kind');
|
|
809
693
|
const runs = [], strays = [], stack = [];
|
|
810
|
-
for (const marker of markers ||
|
|
811
|
-
if (
|
|
812
|
-
if ((color || font) && stack.length) { strays.push(marker); continue; }
|
|
813
|
-
stack.push(marker);
|
|
814
|
-
continue;
|
|
815
|
-
}
|
|
694
|
+
for (const marker of markers || scanInkMarkers(source)) {
|
|
695
|
+
if (!marker.close) { stack.push(marker); continue; }
|
|
816
696
|
const open = stack.pop();
|
|
817
697
|
if (!open) { strays.push(marker); continue; }
|
|
818
|
-
runs.push({ start: open.start, end: marker.end, innerStart: open.end, innerEnd: marker.start,
|
|
819
|
-
...(color ? { hex: open.hex } : font ? {family: open.family} : { mark: open.mark }) });
|
|
698
|
+
runs.push({ start: open.start, end: marker.end, innerStart: open.end, innerEnd: marker.start, mark: open.mark });
|
|
820
699
|
}
|
|
821
700
|
strays.push(...stack);
|
|
822
701
|
runs.sort((a, b) => a.start - b.start);
|
|
@@ -897,7 +776,6 @@ export function isPageBreakLine(text) {
|
|
|
897
776
|
return typeof text === 'string' && /^<!--md-break:v1 page-->\r?$/.test(text);
|
|
898
777
|
}
|
|
899
778
|
|
|
900
|
-
// Also relocated by .toString(); reads only its own regex.
|
|
901
779
|
export function isPageBreakBlock(text) {
|
|
902
780
|
return typeof text === 'string' && /^<!--md-break:v1 page-->\r?\n?$/.test(text);
|
|
903
781
|
}
|
package/dist/spec/md-source.mjs
CHANGED
|
@@ -28,7 +28,11 @@ export function markdownSourcePositions(source, parser) {
|
|
|
28
28
|
}
|
|
29
29
|
return matched;
|
|
30
30
|
});
|
|
31
|
-
|
|
31
|
+
// A core rule may rewrite an inline token's content after its children were parsed (a task item drops
|
|
32
|
+
// its `[ ] `). Map rows from the text the inline rules read.
|
|
33
|
+
const read = new WeakMap();
|
|
34
|
+
inline.parse = function (text, md, env, children) { read.set(children, text); return parser.inline.parse.call(this, text, md, env, children); };
|
|
35
|
+
const {tokens, cells} = markdownTableSources(body, owner, {}, lines);
|
|
32
36
|
const readInline = (children, content, rawOffset) => {
|
|
33
37
|
for (const child of children || []) {
|
|
34
38
|
const span = observed.get(child);
|
|
@@ -50,20 +54,7 @@ export function markdownSourcePositions(source, parser) {
|
|
|
50
54
|
}
|
|
51
55
|
}
|
|
52
56
|
};
|
|
53
|
-
let table = null;
|
|
54
57
|
for (const token of tokens) {
|
|
55
|
-
if (token.type === 'tr_open' && token.map) {
|
|
56
|
-
// GFM unescapes a pipe before parsing a cell. Preserve that one-character projection and
|
|
57
|
-
// consume cells in order, so repeated cells and backticks across a separator never alias.
|
|
58
|
-
const start = lineAt(token.map[0]), end = lineAt(token.map[0] + 1), offsets = [];
|
|
59
|
-
let text = '';
|
|
60
|
-
for (let at = start; at < end; at++) {
|
|
61
|
-
if (source[at] === '\\' && source[at + 1] === '|') at++;
|
|
62
|
-
offsets.push(at); text += source[at] === '\0' ? '\ufffd' : source[at];
|
|
63
|
-
}
|
|
64
|
-
offsets.push(end); table = {text, offsets, at: 0};
|
|
65
|
-
}
|
|
66
|
-
if (token.type === 'tr_close') table = null;
|
|
67
58
|
if (token.map && ['fence', 'code_block', 'html_block', 'reference_definition'].includes(token.type)) {
|
|
68
59
|
const start = lineAt(token.map[0]), end = lineAt(token.map[1]);
|
|
69
60
|
add(start, end);
|
|
@@ -81,29 +72,30 @@ export function markdownSourcePositions(source, parser) {
|
|
|
81
72
|
}
|
|
82
73
|
continue;
|
|
83
74
|
}
|
|
84
|
-
|
|
75
|
+
const content = read.get(token.children) ?? token.content;
|
|
76
|
+
if (token.type !== 'inline' || !content) continue;
|
|
85
77
|
let rawOffset;
|
|
86
78
|
if (token.map) {
|
|
87
|
-
const offsets = [], starts = [], rows =
|
|
79
|
+
const offsets = [], starts = [], spaces = [], rows = content.split('\n');
|
|
88
80
|
let local = 0;
|
|
89
81
|
for (let i = 0; i < rows.length; i++) {
|
|
90
|
-
const start = lineAt(token.map[0] + i), end = lineAt(token.map[0] + i + 1);
|
|
91
|
-
|
|
82
|
+
const start = lineAt(token.map[0] + i), end = lineAt(token.map[0] + i + 1), line = source.slice(start, end).replace(/\0/g, '\ufffd');
|
|
83
|
+
let at = line.indexOf(rows[i]), lead = 0;
|
|
84
|
+
// A container that consumes part of a tab leaves the rest as spaces the source does not hold.
|
|
85
|
+
// They all stand for that tab.
|
|
86
|
+
if (at < 0) { const rest = rows[i].replace(/^ +/, ''); lead = rows[i].length - rest.length; at = line.indexOf(rest); if (!lead || line[at - 1] !== '\t') at = -1; }
|
|
92
87
|
if (at < 0) throw new Error('Markdown inline source mapping failed');
|
|
93
|
-
starts.push(local); offsets.push(start + at); local += rows[i].length + 1;
|
|
88
|
+
starts.push(local); spaces.push(lead); offsets.push(start + at - lead); local += rows[i].length + 1;
|
|
94
89
|
}
|
|
95
90
|
rawOffset = pos => {
|
|
96
91
|
let row = starts.length - 1;
|
|
97
92
|
while (row > 0 && starts[row] > pos) row--;
|
|
98
|
-
return offsets[row] + pos - starts[row];
|
|
93
|
+
return offsets[row] + Math.max(pos - starts[row], spaces[row] - 1);
|
|
99
94
|
};
|
|
100
|
-
} else if (
|
|
101
|
-
|
|
102
|
-
if (at < 0) throw new Error('Markdown table source mapping failed');
|
|
103
|
-
table.at = at + token.content.length;
|
|
104
|
-
rawOffset = pos => table.offsets[at + pos];
|
|
95
|
+
} else if (cells.has(token)) {
|
|
96
|
+
rawOffset = cells.get(token);
|
|
105
97
|
} else continue;
|
|
106
|
-
readInline(token.children,
|
|
98
|
+
readInline(token.children, content, rawOffset);
|
|
107
99
|
}
|
|
108
100
|
literal.sort((a, b) => a.start - b.start || b.end - a.end);
|
|
109
101
|
const merged = [];
|
|
@@ -120,3 +112,54 @@ export function markdownSourcePositions(source, parser) {
|
|
|
120
112
|
};
|
|
121
113
|
return {isText, isComment: (start, end) => comments.get(start) === end, typography: typography.sort((a, b) => a.start - b.start)};
|
|
122
114
|
}
|
|
115
|
+
|
|
116
|
+
// Capture only table-cell provenance while the parser still owns container-adjusted row
|
|
117
|
+
// bounds. Looking for cell text in a whole row aliases literal pipes and quote/list markers.
|
|
118
|
+
export function markdownTableSources(source, parser, environment, lineStarts = null) {
|
|
119
|
+
const cells = new WeakMap();
|
|
120
|
+
if (!source.includes('|')) return {tokens: parser.parse(source, environment), cells};
|
|
121
|
+
const lines = lineStarts || [0];
|
|
122
|
+
if (!lineStarts) for (const row of source.matchAll(/\r\n?|\n/g)) lines.push(row.index + row[0].length);
|
|
123
|
+
const owner = Object.create(parser), block = Object.create(parser.block), ruler = Object.create(parser.block.ruler);
|
|
124
|
+
owner.block = block; block.ruler = ruler;
|
|
125
|
+
ruler.getRules = chain => parser.block.ruler.getRules(chain).map(rule => (state, start, end, silent) => {
|
|
126
|
+
const first = state.tokens.length, matched = rule(state, start, end, silent);
|
|
127
|
+
if (matched && !silent && state.tokens[first]?.type === 'table_open') {
|
|
128
|
+
let cell;
|
|
129
|
+
for (let at = first; at < state.tokens.length; at++) {
|
|
130
|
+
const token = state.tokens[at];
|
|
131
|
+
if (token.type === 'tr_open') {
|
|
132
|
+
const row = token.map[0], from = state.bMarks[row] + state.tShift[row];
|
|
133
|
+
const line = state.src.lastIndexOf('\n', from - 1) + 1;
|
|
134
|
+
cell = tableSource(state.src.slice(from, state.eMarks[row]), lines[row] + from - line);
|
|
135
|
+
} else if (token.type === 'inline' && cell) cells.set(token, cell(token.content));
|
|
136
|
+
else if (token.type === 'tr_close') cell = null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return matched;
|
|
140
|
+
});
|
|
141
|
+
return {tokens: owner.parse(source, environment), cells};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function tableSource(row, base) {
|
|
145
|
+
const trim = row.trimStart();
|
|
146
|
+
base += row.length - trim.length; row = trim.trimEnd();
|
|
147
|
+
const offsets = [], stops = [];
|
|
148
|
+
let text = '', cursor = 0;
|
|
149
|
+
for (let at = 0; at < row.length; at++) {
|
|
150
|
+
if (row[at] === '\\' && row[at + 1] === '|') at++;
|
|
151
|
+
else if (row[at] === '|') stops.push(text.length);
|
|
152
|
+
offsets.push(base + at); text += row[at];
|
|
153
|
+
}
|
|
154
|
+
offsets.push(base + row.length);
|
|
155
|
+
let next = stops[0] === 0 ? 1 : 0;
|
|
156
|
+
if (next) cursor = 1;
|
|
157
|
+
if (stops.at(-1) !== text.length - 1) stops.push(text.length);
|
|
158
|
+
return content => {
|
|
159
|
+
const stop = stops[next++] ?? text.length, raw = text.slice(cursor, stop);
|
|
160
|
+
const start = Math.min(text.length, cursor + raw.length - raw.trimStart().length);
|
|
161
|
+
if (raw.trim() !== content) throw new Error('Markdown table source mapping failed');
|
|
162
|
+
cursor = stop + 1;
|
|
163
|
+
return pos => offsets[start + pos];
|
|
164
|
+
};
|
|
165
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rapier-markdown-kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.5",
|
|
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
|
@@ -407,7 +407,7 @@
|
|
|
407
407
|
border-inline-start:3px dotted var(--md-color-line);
|
|
408
408
|
z-index:0;
|
|
409
409
|
}
|
|
410
|
-
.md-render
|
|
410
|
+
.md-render :is(h1,h2,h3,h4,h5,h6)[data-rapier-list-through]:has(+:is(h1,h2,h3,h4,h5,h6)){--md-list-heading-after:var(--md-gap-heading-before)}
|
|
411
411
|
.md-render li{margin:0 0 calc(var(--md-line) / 4)}
|
|
412
412
|
.md-render li:last-child{margin-bottom:0}
|
|
413
413
|
.md-render li>ul,.md-render li>ol{margin:calc(var(--md-line) / 4) 0 0}
|
|
@@ -499,8 +499,8 @@
|
|
|
499
499
|
vertical-align:middle;
|
|
500
500
|
}
|
|
501
501
|
.md-render p>.math-display-wrap{margin:calc(var(--md-line) / 4) 0 var(--md-gap-block)}
|
|
502
|
-
.md-render
|
|
503
|
-
.md-render
|
|
502
|
+
.md-render p:has(>:is(.math-display-wrap,.rapier-hscroll--math):only-child){margin-bottom:var(--md-gap-block)}
|
|
503
|
+
.md-render p>:is(.math-display-wrap,.rapier-hscroll--math):only-child{margin:0}
|
|
504
504
|
/* Diagram MathML keeps the metrics used by its renderer. */
|
|
505
505
|
.md-render math[display="block"]:not(svg *){font-size:1.12em}
|
|
506
506
|
.md-render math:not([display="block"]):not(svg *){font-size:1em}
|
|
@@ -650,6 +650,9 @@ html[data-invert-image-alpha="off"] :is(img,.md-render svg.rapier-native-flowcha
|
|
|
650
650
|
vertical-align:middle;
|
|
651
651
|
}
|
|
652
652
|
.md-render img[data-rapier-image-size]{width:min(var(--md-image-width),100%);height:auto}
|
|
653
|
+
/* AUTO stays relative to its column. Authored and gesture-owned inline sizing wins;
|
|
654
|
+
Plain reads the intrinsic attributes without rerendering ordinary image blocks. */
|
|
655
|
+
html:not([data-layout="plain"]) .md-render img[data-rapier-image-geometry="auto"]{width:min(var(--rapier-image-auto-width),var(--rapier-image-natural-width));height:auto}
|
|
653
656
|
.md-render img:is([data-rapier-asset],[data-rapier-image-url]):not([data-rapier-asset-state="ready"]){background:color-mix(in srgb,var(--md-color-text) 5%,transparent)}
|
|
654
657
|
|
|
655
658
|
.md-render dl{margin:0 0 var(--md-gap-block)}
|
|
@@ -697,7 +700,7 @@ html[data-invert-image-alpha="off"] :is(img,.md-render svg.rapier-native-flowcha
|
|
|
697
700
|
.md-render [data-md-color="#be185d"]{--md-color-dark:#f9a8d4}
|
|
698
701
|
.md-render [data-md-color="#57606a"]{--md-color-dark:#aab1bb}
|
|
699
702
|
.md-render abbr[title]{text-decoration:underline dotted;text-decoration-color:var(--md-color-text-muted);text-underline-offset:2px;cursor:help}
|
|
700
|
-
.md-render ins{text-decoration:underline;color:var(--md-color-success)}
|
|
703
|
+
.md-render :is(ins,u){text-decoration:underline;color:var(--md-color-success)}
|
|
701
704
|
/* The document owns its selection accent, including on an exported page. */
|
|
702
705
|
.md-render ::selection{background:color-mix(in srgb,var(--md-color-accent) 48%,transparent)}
|
|
703
706
|
.md-render del,.md-render s{color:var(--md-color-text-muted);text-decoration:line-through}
|
|
@@ -747,52 +750,5 @@ html[data-invert-image-alpha="off"] :is(img,.md-render svg.rapier-native-flowcha
|
|
|
747
750
|
.md-render [data-md-break]::after{content:none!important}
|
|
748
751
|
}
|
|
749
752
|
|
|
750
|
-
/* Authored spacing: one body line, native collapsed gaps, and whole-line block boxes. */
|
|
751
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render){
|
|
752
|
-
--md-list-heading-before:var(--md-line);
|
|
753
|
-
--md-list-heading-after:calc(var(--md-line) / 2);
|
|
754
|
-
--md-gap-paragraph:calc(var(--md-space-paragraph,.5) * var(--md-line));
|
|
755
|
-
--md-gap-heading-before:calc(var(--md-space-heading-before,1.5) * var(--md-line));
|
|
756
|
-
--md-gap-heading-after:calc(var(--md-space-heading-after,.5) * var(--md-line));
|
|
757
|
-
--md-gap-block:calc(var(--md-space-block,1) * var(--md-line));
|
|
758
|
-
}
|
|
759
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) :where(p,h1,h2,h3,h4,h5,h6,ul,ol,blockquote,pre,details,dl,figure,.diagram-block,.math-display-wrap,.table-scroll-wrap,.rapier-hscroll,.md-space-block){
|
|
760
|
-
margin-block:var(--md-gap-block);
|
|
761
|
-
height:calc-size(auto,round(up,size,var(--md-line)));
|
|
762
|
-
}
|
|
763
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) .md-space-block{display:flow-root}
|
|
764
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) p{margin-block:0 var(--md-gap-paragraph)}
|
|
765
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) :is(h1,h2,h3,h4,h5,h6){margin-block:var(--md-gap-heading-before) var(--md-gap-heading-after)}
|
|
766
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) :is(h2,h3){line-height:calc(2 * var(--md-line))}
|
|
767
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) :is(p[data-md-picture],p:has(>.math-display-wrap:only-child)){margin-block:var(--md-gap-block)}
|
|
768
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) p[data-md-picture]{line-height:0}
|
|
769
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) :is(li,dd){margin-block:0 var(--md-gap-paragraph)}
|
|
770
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) :is(li:last-child,dd:last-child){margin-block-end:0}
|
|
771
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) dt{margin-block:var(--md-gap-paragraph) 0}
|
|
772
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) blockquote{padding-block:calc(var(--md-line) / 2)}
|
|
773
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) .callout__label{margin-block:0 var(--md-gap-paragraph);height:auto}
|
|
774
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) pre{padding-block:calc(var(--md-line) / 2 - 1px);line-height:var(--md-line)}
|
|
775
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) pre code{display:block;line-height:var(--md-line)}
|
|
776
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) pre[data-rapier-code-lines]{padding-block-start:calc(var(--md-line) * 1.5 - 1px)}
|
|
777
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) details{padding-block:calc(var(--md-line) / 2 - var(--md-hair));}
|
|
778
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) summary{padding-block:0;line-height:var(--md-line)}
|
|
779
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) details[open]>summary{margin-block-end:var(--md-gap-paragraph)}
|
|
780
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) details[open]:not(:has(>:not(summary))){padding-block-end:calc(var(--md-line) * 1.5 - var(--md-hair))}
|
|
781
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) :is(th,td){line-height:var(--md-line);padding-block:calc(var(--md-line) / 2 - var(--md-hair) / 2);
|
|
782
|
-
border:0;border-inline-end:var(--md-hair) solid color-mix(in srgb,var(--md-color-line) 74%,transparent);border-block-end:var(--md-hair) solid color-mix(in srgb,var(--md-color-line) 74%,transparent)}
|
|
783
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) thead th{border-block-end:2px solid var(--md-color-line);padding-block-end:calc(var(--md-line) / 2 + var(--md-hair) / 2 - 2px)}
|
|
784
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) table{margin-block:var(--md-gap-block);border:0;border-collapse:separate;border-spacing:0;outline:calc(2 * var(--md-hair)) solid var(--md-color-line);outline-offset:calc(-2 * var(--md-hair))}
|
|
785
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) thead{outline:calc(2 * var(--md-hair)) solid var(--md-color-line);outline-offset:calc(-2 * var(--md-hair))}
|
|
786
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) :is(.table-scroll-wrap,.rapier-hscroll--table){padding-block:0}
|
|
787
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) :is(.table-scroll-wrap,.rapier-hscroll)>:is(table,pre,.math-display-wrap),
|
|
788
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) p>.math-display-wrap{margin-block:0}
|
|
789
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) hr{margin-block:var(--md-gap-block);height:var(--md-line);box-sizing:border-box}
|
|
790
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render)>:first-child,
|
|
791
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) :is(blockquote,li,td,th,dd,details,.md-space-block)>:first-child{margin-block-start:0}
|
|
792
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) :is(blockquote,td,th,dd,details,.md-space-block)>:last-child{margin-block-end:0}
|
|
793
|
-
@media print{
|
|
794
|
-
:is(.md-render[data-md-space],[data-md-space] .md-render) pre[data-rapier-code-lines]{padding-block-start:calc(var(--md-line) / 2 - 1px)!important}
|
|
795
|
-
}
|
|
796
|
-
|
|
797
753
|
/* Authored run sizes supply their own line box; surrounding block rhythm is unchanged. */
|
|
798
754
|
.md-render [data-md-size]{line-height:normal}
|