rapier-markdown-kit 1.2.44 → 1.2.46

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.
Files changed (41) hide show
  1. package/LICENSE +12 -5
  2. package/README.md +131 -10
  3. package/dist/agent/vendor/BROWSER-ENTITIES.json +36 -0
  4. package/dist/agent/vendor/MARKDOWN-RUNTIME.json +136 -0
  5. package/dist/agent/vendor/MARKDOWNIT.json +582 -0
  6. package/dist/agent/vendor/NOTICE.txt +60 -0
  7. package/dist/agent/vendor/markdown-licenses/entities-LICENSE.txt +11 -0
  8. package/dist/agent/vendor/markdown-licenses/linkify-it-LICENSE.txt +22 -0
  9. package/dist/agent/vendor/markdown-licenses/markdown-it-LICENSE.txt +22 -0
  10. package/dist/agent/vendor/markdown-licenses/mdurl-LICENSE.txt +45 -0
  11. package/dist/agent/vendor/markdown-licenses/punycode.js-LICENSE-MIT.txt +20 -0
  12. package/dist/agent/vendor/markdown-licenses/uc.micro-LICENSE.txt +20 -0
  13. package/dist/agent/vendor/markdown-plugins.json +46 -0
  14. package/dist/agent/vendor/markdown-plugins.mjs +27 -0
  15. package/dist/agent/vendor/markdown-runtime.mjs +13 -0
  16. package/dist/kit/assets.d.mts +106 -0
  17. package/dist/kit/index.d.mts +151 -0
  18. package/dist/kit/layout.d.mts +61 -0
  19. package/dist/kit/ledger/carried.d.mts +12 -6
  20. package/dist/kit/ledger/carried.mjs +1 -1
  21. package/dist/kit/ledger/format.mjs +5 -3
  22. package/dist/kit/ledger/text-merge.mjs +33 -0
  23. package/dist/kit/ledger/transport.mjs +61 -2
  24. package/dist/kit/ledger.d.mts +137 -31
  25. package/dist/kit/markdown-it.d.mts +2 -2
  26. package/dist/kit/marks.d.mts +241 -0
  27. package/dist/kit/marks.mjs +39 -1
  28. package/dist/kit/model.d.mts +191 -0
  29. package/dist/kit/render-entry.d.mts +654 -0
  30. package/dist/kit/render.mjs +8 -7
  31. package/dist/kit/typography.d.mts +42 -0
  32. package/dist/kit/will.d.mts +26 -3
  33. package/dist/math/delimiters.mjs +38 -0
  34. package/dist/math/markdown-rules.mjs +103 -0
  35. package/dist/spec/document-fonts.mjs +2 -10
  36. package/dist/spec/md-assets.mjs +1 -0
  37. package/dist/spec/md-comments.mjs +384 -0
  38. package/dist/spec/md-marks.mjs +9 -0
  39. package/dist/spec/md-parser-config.mjs +463 -0
  40. package/dist/spec/md-parser-runtime.mjs +23 -0
  41. package/package.json +30 -8
@@ -49,8 +49,7 @@ async function _rapierBuildArtifact(options, providedContext) {
49
49
  const opts = options || {};
50
50
  const context = providedContext || _rapierBuildInterchangeContext(opts);
51
51
  const preference = _rapierArtifactPreference('theme', ['system', 'light', 'dark']);
52
- const theme = opts.print ? 'light' : preference === 'system'
53
- ? (globalThis.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : preference;
52
+ const theme = opts.print ? 'light' : preference;
54
53
  const metadata = context.metadata;
55
54
  const base = context.baseName;
56
55
  const work = context.work || opts.work;
@@ -113,8 +112,8 @@ async function _rapierBuildArtifact(options, providedContext) {
113
112
  const styledRoot = work ? await _rapierProjectStyledRootAsync(context.semanticRoot, styledOptions)
114
113
  : _rapierProjectStyledRoot(context.semanticRoot, styledOptions);
115
114
  styledRoot.setAttribute('lang', language);
116
- const darkPaper = theme === 'dark' && _rapierArtifactPreference('invertImageAlpha', ['true', 'false']) !== 'false';
117
- const templatePage = _rapierTemplatePage(context.canonical, settings, styledRoot, darkPaper);
115
+ const paperTheme = _rapierArtifactPreference('invertImageAlpha', ['true', 'false']) !== 'false' ? theme : 'light';
116
+ const templatePage = _rapierTemplatePage(context.canonical, settings, styledRoot, paperTheme);
118
117
  if (completePage && typeof _rapierFinalizeExportCode === 'function') await _rapierFinalizeExportCode(styledRoot);
119
118
  if (!opts.print) _rapierAnnotateExportBoxPolygons(styledRoot);
120
119
  if (completePage || opts.print) {
@@ -204,7 +203,7 @@ async function _rapierBuildArtifact(options, providedContext) {
204
203
  };
205
204
  }
206
205
 
207
- function _rapierTemplatePage(canonical, settings, root, darkPaper = false) {
206
+ function _rapierTemplatePage(canonical, settings, root, paperTheme = 'light') {
208
207
  if (!isTemplatePage(canonical)) return null;
209
208
  const paper = pageSizeOf(settings);
210
209
  let url = '';
@@ -219,9 +218,11 @@ function _rapierTemplatePage(canonical, settings, root, darkPaper = false) {
219
218
  const margins = ['top', 'right', 'bottom', 'left'].map(side => (settings?.geometry?.[side] ?? 1440) / 15);
220
219
  const padding = margins.map(value => value + 'px').join(' ');
221
220
  const ground = 'background:var(--rapier-template-border) 0 0/100% 100% no-repeat';
222
- const shown = darkPaper && globalThis.RapierImageAssets.dataImage(url)?.codec === 'image/svg+xml' ? _rapierDarkPaperSvgUrl(url, '#fafafa') : url;
221
+ const shown = paperTheme !== 'light' && globalThis.RapierImageAssets.dataImage(url)?.codec === 'image/svg+xml' ? _rapierDarkPaperSvgUrl(url, '#fafafa') : url;
222
+ const screenInk = shown !== url ? '@media screen' + (paperTheme === 'system' ? ' and (prefers-color-scheme:dark)' : '')
223
+ + '{:root{--rapier-template-border:url("' + shown + '")}}\n' : '';
223
224
  const css = ':root{--rapier-template-border:url("' + url + '")}\n'
224
- + '@media screen{' + (shown !== url ? ':root{--rapier-template-border:url("' + shown + '")}' : '') + '.rapier-template-border{display:none}body{background:var(--md-color-inset)}.rapier-page{box-sizing:border-box;width:min(100%,' + width + 'px);min-height:' + height + 'px;margin:0 auto;padding:' + padding
225
+ + 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
226
  + ';background:var(--rapier-template-border) 0 0/100% ' + height + 'px repeat-y,var(--md-color-bg);color:var(--md-color-text)}}\n'
226
227
  + '@media screen and (max-width:' + width + 'px){.rapier-page{min-height:auto;aspect-ratio:' + width + '/' + height + ';padding:'
227
228
  + margins.map(value => 'min(' + value + 'px,' + Number((value / width * 100).toFixed(4)) + '%)').join(' ') + ';background-size:100% auto,auto}}\n'
@@ -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;
@@ -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: {pos: number; removed: string; inserted: string}[]):
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,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
+ }
@@ -2,20 +2,12 @@
2
2
  import {markdownParser, installMarkdownImages, normalizeLabel} from './md-assets.mjs';
3
3
  import {splitOpeningFrontmatter} from './frontmatter.mjs';
4
4
  import {readDocumentSettings, documentFont} from './document-settings.mjs';
5
- import {normalizeFontFamily, fontFamilyCss, FONT_STACKS, typography} from './md-marks.mjs';
5
+ import {normalizeFontFamily, fontFamilyCss, FONT_STACKS, typography, fontDataUrl} from './md-marks.mjs';
6
+ export {fontDataUrl};
6
7
 
7
8
  export const CSS_GENERIC_FONT_FAMILIES = Object.freeze(['serif', 'sans-serif', 'monospace', 'cursive', 'fantasy', 'system-ui',
8
9
  'ui-serif', 'ui-sans-serif', 'ui-monospace', 'ui-rounded', 'emoji', 'math', 'fangsong', 'initial', 'revert', 'revert-layer']);
9
10
 
10
- export function fontDataUrl(url) {
11
- if (typeof url !== 'string') return null;
12
- const match = /^data:font\/(woff2|woff|ttf|otf);base64,([A-Za-z0-9+/]+={0,2})$/i.exec(url);
13
- if (!match || match[2].length % 4) return null;
14
- const padding = match[2].endsWith('==') ? 2 : match[2].endsWith('=') ? 1 : 0;
15
- const byteLength = match[2].length / 4 * 3 - padding;
16
- return byteLength >= 12 ? {format: match[1].toLowerCase(), byteLength, data: match[2]} : null;
17
- }
18
-
19
11
  function parserFor(parser) {
20
12
  const md = installMarkdownImages(markdownParser(parser));
21
13
  if (!md.__rapierFontsInstalled) {
@@ -9,6 +9,7 @@ const cached = [];
9
9
  let preparedDefinitions = [];
10
10
  let configured = null, splitSource = null;
11
11
  export function configureParser(parser, split) { configured = parser; splitSource = split; cached.length = 0; preparedDefinitions = []; }
12
+ export const configuredMarkdownParser = () => configured;
12
13
  function fail(reason) { throw new Error(reason); }
13
14
  export const normalizeLabel = label => String(label).trim().replace(/\s+/g, ' ').toLowerCase().toUpperCase();
14
15
  export const assetTitle = value => String(value || '').replace(/[\r\n]/g, ' ').replace(/\0/g, '\ufffd');