rapier-markdown-kit 1.1.79 → 1.1.81

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.
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  // The document's renderer. DOM, codecs and host state are explicit inputs.
3
3
  function createRenderer(runtime) {
4
- const {RAPIER_COLOR_CLOSE, RapierLedgerCarried, RapierPageReturnAddress, RapierTextCodec, _rapierArtifactHighlight, _rapierArtifactLexerScript, _rapierArtifactMarkLexed, _rapierArtifactPreference, _rapierArtifactStyles, _rapierBlobDataUrl, _rapierBuildInterchangeContext, _rapierDocumentNameIsAdmissible, _rapierLedgerParts, _rapierDrawReadSVGRecipe, _rapierDrawShapeProfileFor, _rapierFillDiagram, _rapierFormatColorOpen, _rapierLanguageClass, _rapierLineStartOffsets, _rapierMarkdownEnvironment, _rapierPortableHtml, _rapierPrepareInterchangeContext, _rapierProjectPortableRoot, _rapierSourceCharEscaped, _rapierSourceLineSpan, crypto, document, escapeRapierHtmlText, globalThis, rapierConfirm, sanitizeRapierHtml} = runtime;
4
+ const {RAPIER_COLOR_CLOSE, RapierLedgerCarried, RapierPageReturnAddress, RapierTextCodec, _rapierArtifactHighlight, _rapierArtifactLexerScript, _rapierArtifactMarkLexed, _rapierArtifactPreference, _rapierArtifactStyles, _rapierBlobDataUrl, _rapierBuildInterchangeContext, _rapierDocumentNameIsAdmissible, _rapierLedgerParts, _rapierDrawReadSVGRecipe, _rapierDrawShapeProfileFor, _rapierFillDiagram, _rapierFormatColorOpen, _rapierLanguageClass, _rapierLineStartOffsets, _rapierMarkdownEnvironment, _rapierPortableHtml, _rapierPrepareInterchangeContext, _rapierProjectPortableRoot, _rapierProviders, _rapierSourceCharEscaped, _rapierSourceLineSpan, crypto, document, escapeRapierHtmlText, globalThis, rapierConfirm, sanitizeRapierHtml} = runtime;
5
5
  let md = runtime.md;
6
6
  async function render(source, options = {}) {
7
7
  if (typeof source !== 'string') throw new TypeError('Document source must be a string');
@@ -32,7 +32,7 @@ async function _rapierBuildArtifact(options, providedContext) {
32
32
 
33
33
  // The semantic export root is detached and has never entered the live visibility queue.
34
34
  // Await the same materialization owner before any projection clones or serializes it.
35
- for (const diagram of context.semanticRoot.querySelectorAll('.diagram-block[data-diagram-src]')) {
35
+ for (const diagram of opts.kind === 'page' ? [] : context.semanticRoot.querySelectorAll('.diagram-block[data-diagram-src]')) {
36
36
  await _rapierFillDiagram(diagram, true);
37
37
  }
38
38
 
@@ -41,21 +41,23 @@ async function _rapierBuildArtifact(options, providedContext) {
41
41
  const styledRoot = _rapierProjectStyledRoot(context.semanticRoot, {
42
42
  metadata,
43
43
  print: !!opts.print,
44
+ sourceCode: opts.kind === 'page',
44
45
  baseName: base,
45
46
  });
46
47
  // _rapierProjectStyledRoot already returns the sanitized projection.
47
48
  // Print never emits the layout reflow script (see below), so a print artifact has no use for
48
49
  // this annotation -- skip it there rather than pay the cost for numbers nothing will read.
49
- if (!opts.print) _rapierAnnotateExportBoxPolygons(styledRoot);
50
+ if (!opts.print && opts.kind !== 'page') _rapierAnnotateExportBoxPolygons(styledRoot);
50
51
  // Share adds its nearest-side no-script float and long-code cap here. Offline-image
51
52
  // admission above and the page policy below belong to this common writer, not its callers.
52
53
  if (typeof opts.afterRoot === 'function') opts.afterRoot(styledRoot);
53
54
  const carrier = opts.kind === 'standalone' ? await _rapierSharedSourceCarrier(context, styledRoot) : '';
54
- const bodyHtml = styledRoot.innerHTML;
55
+ const bodyHtml = opts.kind === 'page' ? _rapierPageHtml(styledRoot) : styledRoot.innerHTML;
55
56
  const theme = opts.print
56
57
  ? 'light'
58
+ : opts.kind === 'page' ? 'system'
57
59
  : _rapierArtifactPreference('theme', ['system', 'light', 'dark']);
58
- const highlights = _rapierArtifactPreference('highlights', ['accent', 'standard']);
60
+ const highlights = opts.kind === 'page' ? 'standard' : _rapierArtifactPreference('highlights', ['accent', 'standard']);
59
61
  const versionMeta = document.querySelector('meta[name="rapier-version"]');
60
62
  const version = versionMeta && versionMeta.content ? versionMeta.content.trim() : '0.0.0';
61
63
  const bodyClass = theme === 'light' ? 'light' : '';
@@ -66,20 +68,18 @@ async function _rapierBuildArtifact(options, providedContext) {
66
68
  // .md-render set and the highlight rules, so a numbered list, a checkbox, a callout and a
67
69
  // table are drawn in an exported page exactly as they are drawn here. The one caller with
68
70
  // rules of its own -- Share, for the no-script float it alone writes -- appends them.
69
- // The type faces ride only where they can load. A written page's policy is default-src
70
- // 'none' with no font-src, so a data: font in one is fetched by nothing and would be tens of
71
- // kilobytes of bytes no reader ever sees; those pages take the reader's own sans matched to
72
- // Geist's metrics (_rapierArtifactStyles), which is the one difference between a Rapier
73
- // document and its exported page. A print artifact renders inside this page, under this
74
- // page's policy, so it keeps the real faces.
75
- // The print page carries its faces as data: faces of its own, and a Will's carriers' font among them (the secure-runtime host prints this
76
- // page, not the one it was made in). Its policy alone has font-src data:, which reaches no network, whether or not the page has a Will:
77
- // with it only for a Will the same document would print in its faces with the Will and in the reader's own without (measured).
71
+ // Diagram geometry was measured with these exact fonts. Carry the same data fonts into
72
+ // offline pages; a fallback face would change labels inside fixed renderer geometry.
73
+ const diagramFonts = !!styledRoot.querySelector('svg.rapier-diagram,svg.rapier-native-flowchart');
74
+ const includeFonts = !!opts.print || diagramFonts;
75
+ const zenFontCss = styledRoot.querySelector('svg[aria-roledescription="zenuml"]')
76
+ ? (_rapierProviders?.mermaid?.exportFontCss?.() || '') : '';
78
77
  const willFont = opts.print && opts.willFont ? opts.willFont : null;
79
78
  const settings = _rapierDocumentSettingsOf(context.canonical);
80
79
  const settingsCss = _rapierDocumentSettingsCss(settings);
81
80
  const pageTitle = settings && settings.title ? globalThis.RapierMarkdownSpec.documentTitle(settings) : metadata.filename;
82
- const css = _rapierArtifactStyles(theme, !!opts.print, !!opts.print || opts.kind === 'standalone')
81
+ const css = _rapierArtifactStyles(theme, includeFonts, !!opts.print || opts.kind === 'standalone', opts.kind === 'page')
82
+ + (zenFontCss ? '\n\n' + zenFontCss : '')
83
83
  + (opts.extraCss ? '\n\n' + opts.extraCss : '')
84
84
  + (willFont ? '\n\n@font-face{font-family:' + willFont.family + ';src:url(' + await _rapierBlobDataUrl(new Blob([willFont.bytes], { type: 'font/ttf' })) + ') format("truetype")}' : '')
85
85
  + (settingsCss ? '\n' + settingsCss : '');
@@ -90,11 +90,11 @@ async function _rapierBuildArtifact(options, providedContext) {
90
90
  // The reflow script is written first, because whether it exists decides the policy. A page that carries no script says so: script-src 'none' refuses every
91
91
  // script outright. Declaring a nonce a page has no use for is a widening, small but real -- anything that could inject markup into the file could read the
92
92
  // nonce out of it and be admitted.
93
- const layoutScript = !opts.print ? _rapierArtifactLayoutScript(styledRoot, nonce) : '';
93
+ const layoutScript = !opts.print && opts.kind !== 'page' ? _rapierArtifactLayoutScript(styledRoot, nonce) : '';
94
94
  // The lexer for the page's code, under the same nonce, only when a block earned it: the CPU
95
95
  // spans are the first paint, and where the reader's browser has WebGPU the lexer repaints them.
96
- const lexerScript = !opts.print ? _rapierArtifactLexerScript(styledRoot, nonce) : '';
97
- const inkScript = !opts.print ? _rapierArtifactInkScript(styledRoot, nonce) : '';
96
+ const lexerScript = !opts.print && opts.kind !== 'page' ? _rapierArtifactLexerScript(styledRoot, nonce) : '';
97
+ const inkScript = !opts.print && opts.kind !== 'page' ? _rapierArtifactInkScript(styledRoot, nonce) : '';
98
98
  const page = '<!DOCTYPE html>\n'
99
99
  + '<html lang="en" data-rapier-theme="' + theme + '" data-highlights="' + highlights + '">\n'
100
100
  + '<head>\n<meta charset="UTF-8">\n' + (carrier ? RapierPageReturnAddress.PAGE_SEED + '\n' : '')
@@ -104,7 +104,7 @@ async function _rapierBuildArtifact(options, providedContext) {
104
104
  // someone's page shows whole, with no inner scroll. Any origin may read the height: the page is the
105
105
  // document, published by the person who exported it.
106
106
  + '<meta name="responsive-embedded-sizing" content="allow-origins=*">\n'
107
- + '<meta http-equiv="Content-Security-Policy" content="' + _rapierExportedPageCsp(layoutScript || lexerScript || inkScript ? nonce : '', !!opts.print) + '">\n'
107
+ + '<meta http-equiv="Content-Security-Policy" content="' + _rapierExportedPageCsp(layoutScript || lexerScript || inkScript ? nonce : '', includeFonts) + '">\n'
108
108
  + '<meta name="referrer" content="no-referrer">\n'
109
109
  + '<meta name="generator" content="Rapier ' + escapeRapierHtmlText(version) + '">\n'
110
110
  + '<title>' + escapeRapierHtmlText(pageTitle) + '</title>\n'
@@ -116,12 +116,31 @@ async function _rapierBuildArtifact(options, providedContext) {
116
116
  html: page,
117
117
  filename: base + '.html',
118
118
  bodyHtml,
119
+ css,
119
120
  docClass,
120
121
  metadata,
121
122
  documentTitle: settings && settings.title ? pageTitle : '',
122
123
  };
123
124
  }
124
125
 
126
+ // Page files have one byte representation across native and inert DOM hosts. Attribute
127
+ // insertion order and a host's optional entity escapes are not document content.
128
+ function _rapierPageHtml(root) {
129
+ const voidTags = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
130
+ const children = node => Array.from(node.childNodes || [], write).join('');
131
+ function write(node) {
132
+ if (node.nodeType === 3) return escapeRapierHtmlText(node.nodeValue || '');
133
+ if (node.nodeType !== 1) return '';
134
+ // This writer emits text/html; the HTML reader restores SVG's camel-cased names.
135
+ const tag = node.localName.toLowerCase();
136
+ const attributes = Array.from(node.attributes).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)
137
+ .map(attribute => ' ' + attribute.name + '="' + escapeRapierHtmlText(attribute.value) + '"').join('');
138
+ const open = '<' + tag + attributes + '>';
139
+ return voidTags.has(tag) ? open : open + children(node) + '</' + tag + '>';
140
+ }
141
+ return children(root);
142
+ }
143
+
125
144
  function _rapierProjectStyledRoot(semanticRoot, options) {
126
145
  const opts = options || {};
127
146
  const root = semanticRoot.cloneNode(true);
@@ -138,11 +157,12 @@ function _rapierProjectStyledRoot(semanticRoot, options) {
138
157
  if (code) {
139
158
  const lang = _rapierLanguageClass(code) || String(metadata.codeLang || '');
140
159
  code.classList.add('artifact-code');
141
- if (metadata.docKind === 'code') { _rapierPaintCode(code, _rapierArtifactHighlight(code.textContent || '', lang)); _rapierArtifactMarkLexed(code, lang); }
160
+ if (metadata.docKind === 'code' && !opts.sourceCode) { _rapierPaintCode(code, _rapierArtifactHighlight(code.textContent || '', lang)); _rapierArtifactMarkLexed(code, lang); }
142
161
  if (lang) code.classList.add('language-' + String(lang).replace(/[^a-z0-9_-]/gi, ''));
143
162
  }
144
163
  } else {
145
164
  root.querySelectorAll('pre > code').forEach(code => {
165
+ if (opts.sourceCode) return;
146
166
  const lang = _rapierLanguageClass(code) || 'text';
147
167
  _rapierPaintCode(code, _rapierArtifactHighlight(code.textContent || '', lang));
148
168
  _rapierArtifactMarkLexed(code, lang);
@@ -184,7 +204,7 @@ function _rapierProjectStyledRoot(semanticRoot, options) {
184
204
  _rapierPrefixPortableAnchors(root, opts.baseName || metadata.filename || 'document');
185
205
 
186
206
  const clean = document.createElement('div');
187
- clean.innerHTML = sanitizeRapierHtml(root.innerHTML, 'export');
207
+ clean.innerHTML = sanitizeRapierHtml(root.innerHTML, opts.sourceCode ? 'source' : 'export');
188
208
  return clean;
189
209
  }
190
210
 
@@ -370,7 +370,8 @@ export function serializeAsset(asset) {
370
370
  return '[' + asset.label + ']: ' + asset.url + title;
371
371
  }
372
372
  // Pure: re-derived at commit against the moved document; appends only at the text's end.
373
- export function appendAssetText(source, asset) {
373
+ // A replay supplies the retained definition's line ending instead of choosing one from later prose.
374
+ export function appendAssetText(source, asset, lineEnding) {
374
375
  if (typeof source !== 'string' || !asset || normalizeLabel(asset.label) !== asset.id) return fail('image_source_invalid');
375
376
  const parsed = documentAssets(source), title = assetTitle(asset.title);
376
377
  const existing = [...parsed.assets.values()].find(row => row.url === asset.url && row.title === title);
@@ -382,7 +383,7 @@ export function appendAssetText(source, asset) {
382
383
  return {source, id: asset.id, reference: asset.label, added: false, suffix: ''};
383
384
  }
384
385
  if (parsed.assets.size >= IMAGE_LIMITS.assets) return fail('image_asset_count_limit');
385
- const eol = /\r\n|\n|\r/.exec(source)?.[0] || '\n', tail = /(?:\r\n?|\n)[ \t]*$/.exec(source);
386
+ const eol = lineEnding || /\r\n|\n|\r/.exec(source)?.[0] || '\n', tail = /(?:\r\n?|\n)[ \t]*$/.exec(source);
386
387
  const separator = !source || tail && /[\r\n][ \t]*$/.test(source.slice(0, tail.index)) ? '' : tail ? eol : eol + eol;
387
388
  const suffix = separator + block + eol, nextSource = source + suffix;
388
389
  if (nextSource.length > IMAGE_LIMITS.sourceChars) return fail('image_source_limit');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rapier-markdown-kit",
3
- "version": "1.1.79",
3
+ "version": "1.1.81",
4
4
  "description": "Read, render, write and style Self-contained Markdown, and carry its optional edit ledger: the shared HTML writer, semantic renderer, stylesheet, layout, marks, pictures, Will/1, replay, authorship and offline merge. MIT, no npm dependencies.",
5
5
  "keywords": [
6
6
  "markdown",
package/style.css CHANGED
@@ -87,6 +87,36 @@
87
87
  --md-diagram-c7:light-dark(#5eac56,#002104);
88
88
  --md-diagram-c8:light-dark(#d485fc,#330033);
89
89
  --md-diagram-c9:light-dark(#c9b49b,#4f3626);
90
+
91
+ --md-mermaid-surface:light-dark(#f2f1ee,#171b1e);
92
+ --md-mermaid-border:light-dark(#757b80,#929ba1);
93
+ --md-mermaid-grid:light-dark(#d0d4d6,#424a4f);
94
+ --md-mermaid-muted:light-dark(#525a60,#b7c0c5);
95
+ --md-mermaid-on-series:light-dark(#fff8ea,#11181c);
96
+ --md-mermaid-series-1:light-dark(#176e85,#6bc2d5);
97
+ --md-mermaid-series-2:light-dark(#b55b2a,#f1a172);
98
+ --md-mermaid-series-3:light-dark(#7652a0,#bda0e1);
99
+ --md-mermaid-series-4:light-dark(#34775c,#7fc4a4);
100
+ --md-mermaid-series-5:light-dark(#a34f68,#e79eb4);
101
+ --md-mermaid-series-6:light-dark(#896e23,#d8be68);
102
+ --md-mermaid-series-7:light-dark(#426caa,#92b7ed);
103
+ --md-mermaid-series-8:light-dark(#876144,#cba886);
104
+ --md-mermaid-series-9:light-dark(#55703a,#afc88e);
105
+ --md-mermaid-series-10:light-dark(#94633c,#e4b185);
106
+ --md-mermaid-series-11:light-dark(#8a5186,#d5a0cf);
107
+ --md-mermaid-series-12:light-dark(#476d70,#91b9ba);
108
+ --md-mermaid-tint-1:light-dark(#e0edf1,#1b3941);
109
+ --md-mermaid-tint-2:light-dark(#f5e6dc,#472e21);
110
+ --md-mermaid-tint-3:light-dark(#ebe4f2,#342941);
111
+ --md-mermaid-tint-4:light-dark(#e2eee7,#223b2e);
112
+ --md-mermaid-tint-5:light-dark(#f3e1e8,#432a33);
113
+ --md-mermaid-tint-6:light-dark(#f3edd9,#40391f);
114
+ --md-mermaid-tint-7:light-dark(#e2eaf6,#26354a);
115
+ --md-mermaid-tint-8:light-dark(#eee5dd,#3b3129);
116
+ --md-mermaid-tint-9:light-dark(#e8eddf,#303921);
117
+ --md-mermaid-tint-10:light-dark(#f1e5d9,#433122);
118
+ --md-mermaid-tint-11:light-dark(#f0e2ed,#3e2a3b);
119
+ --md-mermaid-tint-12:light-dark(#e2ecec,#263a3b);
90
120
  }
91
121
  .md-render{color-scheme:light}
92
122
  /* Geist and Geist Mono both have a 530/1000 x-height. Preserve their size and match a
@@ -135,6 +165,10 @@
135
165
  margin: 0 0 var(--md-line);
136
166
  max-width: 100%;
137
167
  overflow: auto;
168
+ padding-block: 0.55rem 0.85rem;
169
+ background:
170
+ linear-gradient(var(--md-diagram-node-line), var(--md-diagram-node-line)) top / 100% 1px no-repeat,
171
+ linear-gradient(var(--md-diagram-node-line), var(--md-diagram-node-line)) bottom / 100% 1px no-repeat;
138
172
  }
139
173
  :where(.md-render) .diagram-block .diagram-source {
140
174
  font-family: var(--md-font-mono);
@@ -145,7 +179,12 @@
145
179
  margin: 0;
146
180
  white-space: pre-wrap;
147
181
  }
148
- :where(.md-render) .diagram-block[data-diagram-state="ready"] .diagram-source {
182
+ /* A drawing in progress keeps its source for the reader and for export, but does not flash
183
+ the fence across the page. The measured height, once known, holds the place. */
184
+ :where(.md-render) .diagram-block:is([data-diagram-state="idle"],[data-diagram-state="pending"]) {
185
+ min-height: 5.25rem;
186
+ }
187
+ :where(.md-render) .diagram-block:is([data-diagram-state="ready"],[data-diagram-state="idle"],[data-diagram-state="pending"]) .diagram-source {
149
188
  position: absolute;
150
189
  width: 1px;
151
190
  height: 1px;
@@ -159,70 +198,21 @@
159
198
  :where(.md-render) .diagram-block .diagram-cache {
160
199
  max-width: 100%;
161
200
  }
162
- /* Mermaid owns its geometry and typography. Only known paint roles are themed here;
163
- other families keep their generator's matching fill and ink. Authored inline pairs win. */
201
+ /* Mermaid owns its geometry and typography. Its reference palette is projected onto SVG paint
202
+ at admission; only renderer paint omissions are corrected here. */
164
203
  :where(.md-render) svg.rapier-native-flowchart{display:block;margin-inline:auto;height:auto}
165
- :where(.md-render) svg.rapier-diagram{display:block;margin-inline:auto;height:auto}
166
-
167
- /* Recolour connectors without changing widths, dashes, marker contours or hollow interiors. */
168
- :where(.md-render) svg.rapier-diagram:is([aria-roledescription^="flowchart"],[aria-roledescription="classDiagram"],[aria-roledescription="stateDiagram"],[aria-roledescription="requirement"],[aria-roledescription="er"]) :is(path.flowchart-link,path.relation,path.relationshipLine,path.transition){stroke:var(--md-diagram-line)!important}
169
- :where(.md-render) svg.rapier-diagram:is([aria-roledescription^="flowchart"],[aria-roledescription="classDiagram"],[aria-roledescription="stateDiagram"],[aria-roledescription="requirement"],[aria-roledescription="er"],[aria-roledescription="sequence"],[aria-roledescription="c4"],[aria-roledescription="journey"]) marker{fill:var(--md-diagram-arrow)!important}
170
-
171
- /* Sequence surfaces and their labels move together between paper colours. */
172
- :where(.md-render) svg.rapier-diagram[aria-roledescription="sequence"] :is(rect.actor,rect.activation0,rect.activation1,rect.activation2,polygon.labelBox){fill:var(--md-diagram-node-fill)!important;stroke:var(--md-diagram-node-line)!important}
173
- :where(.md-render) svg.rapier-diagram[aria-roledescription="sequence"] :is(text.actor,text.labelText,text.loopText,text.messageText,text.sectionTitle),
174
- :where(.md-render) svg.rapier-diagram[aria-roledescription="sequence"] :is(text.actor,text.labelText,text.loopText,text.messageText,text.sectionTitle) tspan{fill:var(--md-color-text)!important}
175
- :where(.md-render) svg.rapier-diagram[aria-roledescription="sequence"] :is(text.sequenceNumber,text.sequenceNumber tspan){fill:var(--md-color-bg)!important}
176
- :where(.md-render) svg.rapier-diagram[aria-roledescription="sequence"] rect.note{fill:var(--md-color-inset)!important;stroke:var(--md-diagram-node-line)!important}
177
- :where(.md-render) svg.rapier-diagram[aria-roledescription="sequence"] :is(text.noteText,text.noteText tspan){fill:var(--md-color-text)!important}
178
- :where(.md-render) svg.rapier-diagram[aria-roledescription="sequence"] :is(line.messageLine0,line.messageLine1,path.messageLine0,path.messageLine1){stroke:var(--md-diagram-line)!important}
179
- :where(.md-render) svg.rapier-diagram[aria-roledescription="sequence"] :is(line.actor-line,line.loopLine){stroke:var(--md-diagram-inner-line)!important}
180
-
181
- /* A state begins with a dot, ends with a ring, and forks through a bar. */
182
- :where(.md-render) svg.rapier-diagram[aria-roledescription="stateDiagram"] :is(circle.state-start,rect.fork-join,circle.end-state-inner){fill:var(--md-color-text)!important;stroke:var(--md-color-text)!important}
183
- :where(.md-render) svg.rapier-diagram[aria-roledescription="stateDiagram"] circle.state-end{fill:var(--md-color-bg)!important;stroke:var(--md-color-text)!important}
184
- /* Compound-state frames follow the paper; their child states keep their own surfaces and ink. */
185
- :where(.md-render) svg.rapier-diagram[aria-roledescription="stateDiagram"] g.statediagram-cluster>rect.inner{fill:var(--md-color-bg)!important}
186
- :where(.md-render) svg.rapier-diagram[aria-roledescription="stateDiagram"] g.statediagram-cluster>g>rect.outer{fill:var(--md-diagram-node-fill)!important}
187
- :where(.md-render) svg.rapier-diagram[aria-roledescription="stateDiagram"] g.statediagram-cluster>.cluster-label :is(text,tspan){fill:var(--md-color-text)!important}
188
- :where(.md-render) svg.rapier-diagram[aria-roledescription="stateDiagram"] g.statediagram-cluster>.cluster-label :is(foreignObject,.nodeLabel){color:var(--md-color-text)!important}
204
+ :where(.md-render) svg.rapier-diagram{display:block;margin-inline:auto;height:auto;text-rendering:optimizeLegibility}
189
205
 
190
- /* C4 node colours include their own label ink. Outside captions and connections use the page. */
191
- :where(.md-render) svg.rapier-diagram[aria-roledescription="c4"] text:not(.c4-shape *){fill:var(--md-color-text)!important}
192
- :where(.md-render) svg.rapier-diagram[aria-roledescription="c4"] :is(line,path)[stroke]:not(.c4-shape *):not(marker *):not(symbol *){stroke:var(--md-diagram-line)!important}
193
- :where(.md-render) svg.rapier-diagram[aria-roledescription="c4"] rect[fill="none"][stroke]:not(.c4-shape *){stroke:var(--md-diagram-node-line)!important}
194
-
195
- /* Journey cards pair their surface with both HTML and SVG label branches; faces keep their paints. */
196
- :where(.md-render) svg.rapier-diagram[aria-roledescription="journey"] rect:is(.journey-section,.task){fill:var(--md-diagram-node-fill)!important}
197
- :where(.md-render) svg.rapier-diagram[aria-roledescription="journey"] rect:is(.journey-section,.task)+switch :is(text,tspan){fill:var(--md-color-text)!important}
198
- :where(.md-render) svg.rapier-diagram[aria-roledescription="journey"] rect:is(.journey-section,.task)+switch foreignObject{color:var(--md-color-text)!important}
199
- :where(.md-render) svg.rapier-diagram[aria-roledescription="journey"]>text,
200
- :where(.md-render) svg.rapier-diagram[aria-roledescription="journey"] text.legend{fill:var(--md-color-text)!important}
201
- :where(.md-render) svg.rapier-diagram[aria-roledescription="journey"]>line{stroke:var(--md-diagram-line)!important}
202
- :where(.md-render) svg.rapier-diagram[aria-roledescription="journey"] line.task-line{stroke:var(--md-diagram-inner-line)!important}
203
-
204
- /* Gantt paint selectors name their element type: journey also uses the class "task" on text. */
205
- :where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] rect:is(.section0,.section2){fill:var(--md-diagram-node-fill)!important}
206
- :where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] rect:is(.section1,.section3){fill:var(--md-color-bg)!important}
207
- :where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] rect.task{fill:var(--md-diagram-c1)!important;stroke:var(--md-diagram-c1)!important}
208
- :where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] rect:is(.active0,.active1,.active2,.active3){fill:var(--md-diagram-c3)!important;stroke:var(--md-diagram-c3)!important}
209
- :where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] rect:is(.done0,.done1,.done2,.done3){fill:var(--md-diagram-node-line)!important;stroke:var(--md-diagram-node-line)!important}
210
- :where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] rect:is(.crit0,.crit1,.crit2,.crit3,.activeCrit0,.activeCrit1,.activeCrit2,.activeCrit3,.doneCrit0,.doneCrit1,.doneCrit2,.doneCrit3){fill:var(--md-diagram-c6)!important;stroke:var(--md-diagram-c6)!important}
211
- :where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] text.taskText{fill:var(--md-diagram-on-colour)!important}
212
- :where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] text:is(.taskTextOutsideRight,.taskTextOutsideLeft,.sectionTitle,.titleText){fill:var(--md-color-text)!important}
213
- :where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] .grid .tick line{stroke:var(--md-diagram-inner-line)!important}
214
- :where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] .grid .tick text{fill:var(--md-diagram-quiet)!important}
215
- :where(.md-render) svg.rapier-diagram[aria-roledescription="gantt"] line.today{stroke:var(--md-diagram-c6)!important}
216
-
217
- /* Outside labels never acquire the ink intended for a coloured slice or branch. */
218
- :where(.md-render) svg.rapier-diagram[aria-roledescription="pie"] text.pieTitleText,
219
- :where(.md-render) svg.rapier-diagram[aria-roledescription="pie"] g.legend text,
220
- :where(.md-render) svg.rapier-diagram[aria-roledescription="timeline"]>text{fill:var(--md-color-text)!important}
221
- :where(.md-render) svg.rapier-diagram[aria-roledescription="timeline"] g.lineWrapper>line{stroke:var(--md-diagram-line)!important}
222
- :where(.md-render) svg.rapier-diagram[aria-roledescription="gitGraph"] rect.commit-label-bkg{fill:var(--md-color-bg)!important}
223
- :where(.md-render) svg.rapier-diagram[aria-roledescription="gitGraph"] text.commit-label{fill:var(--md-diagram-quiet)!important}
224
- :where(.md-render) svg.rapier-diagram[aria-roledescription="gitGraph"] polygon.tag-label-bkg{fill:var(--md-diagram-node-fill)!important;stroke:var(--md-diagram-node-line)!important}
225
- :where(.md-render) svg.rapier-diagram[aria-roledescription="gitGraph"] text.tag-label{fill:var(--md-color-text)!important}
206
+ /* Radar gives these SVG labels a color declaration; SVG text needs a fill. */
207
+ :where(.md-render) svg.rapier-diagram[aria-roledescription="radar"] :is(.radarTitle,.radarAxisLabel){fill:currentColor}
208
+ /* Multiplying a ribbon by black paper makes its entire flow disappear. */
209
+ :where(.md-render) svg.rapier-diagram[aria-roledescription="sankey"] .link{mix-blend-mode:normal!important}
210
+ /* Captions keep their baseline while masking chart marks behind their letters. */
211
+ :where(.md-render) svg.rapier-diagram[aria-roledescription="c4"] text:not(.c4-shape *),
212
+ :where(.md-render) svg.rapier-diagram[aria-roledescription="architecture"] .architecture-service text,
213
+ :where(.md-render) svg.rapier-diagram[aria-roledescription="wardley"] .wardley-node-label,
214
+ :where(.md-render) svg.rapier-diagram[aria-roledescription="xychart"] [class^="line-plot-"]>.labels>text,
215
+ :where(.md-render) svg.rapier-diagram[aria-roledescription="cynefin"] :is(text.cynefinSubtitle,text.cynefinArrowLabel,text.cynefinDomainLabel){paint-order:stroke fill;stroke:var(--md-color-bg)!important;stroke-width:2.5px;stroke-linejoin:round}
226
216
 
227
217
  /* Generator styles are removed at admission; these owned keyframes animate only dash offset. */
228
218
  @keyframes rapier-mermaid-dash{from{stroke-dashoffset:900}to{stroke-dashoffset:0}}
@@ -243,6 +233,9 @@
243
233
  font-size: 0.88em;
244
234
  opacity: .78;
245
235
  }
236
+ :where(.md-render) .diagram-block--error .diagram-source {
237
+ max-height: 9rem;
238
+ }
246
239
 
247
240
  /* The line model (the way a Word document reads). One line, --md-line, is the body's line box: 1.75rem, 1.6 lines
248
241
  of the 1.1rem body, a whole 28 px at the browser's 16 px root so baselines land on pixels. Every block ends one