@zenuml/core 4.8.0 → 4.9.0

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,6 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
  import antlr4 from 'antlr4';
3
3
  import { marked } from 'marked';
4
+ import colorString from 'color-string';
4
5
  import { readFileSync, statSync, mkdirSync, writeFileSync, watch } from 'node:fs';
5
6
  import { resolve, join, dirname, basename, extname } from 'node:path';
6
7
 
@@ -1606,7 +1607,7 @@ class sequenceParserListener extends antlr4.tree.ParseTreeListener {
1606
1607
 
1607
1608
  // Generated from java-escape by ANTLR 4.11.1
1608
1609
  // jshint ignore: start
1609
- const serializedATN = [4,1,81,658,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,
1610
+ const serializedATN = [4,1,81,659,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,
1610
1611
  4,2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,
1611
1612
  2,13,7,13,2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,19,2,
1612
1613
  20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,26,7,26,2,27,
@@ -1655,17 +1656,17 @@ const serializedATN = [4,1,81,658,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,
1655
1656
  1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,
1656
1657
  55,1,55,1,55,1,55,5,55,621,8,55,10,55,12,55,624,9,55,1,56,1,56,1,56,1,56,
1657
1658
  1,56,1,56,1,56,1,56,3,56,634,8,56,1,57,1,57,3,57,638,8,57,1,57,3,57,641,
1658
- 8,57,1,58,1,58,1,58,1,58,3,58,647,8,58,1,59,1,59,1,59,1,59,1,60,4,60,654,
1659
- 8,60,11,60,12,60,655,1,60,0,1,110,61,0,2,4,6,8,10,12,14,16,18,20,22,24,26,
1660
- 28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,
1661
- 76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,
1662
- 118,120,0,13,2,0,8,8,20,20,2,0,9,9,19,19,2,0,62,62,67,69,2,0,11,11,24,24,
1663
- 2,0,62,62,67,67,1,0,25,27,1,0,23,24,1,0,19,22,1,0,17,18,1,0,63,64,1,0,37,
1664
- 38,1,0,68,69,2,0,62,62,66,67,741,0,141,1,0,0,0,2,143,1,0,0,0,4,164,1,0,0,
1665
- 0,6,166,1,0,0,0,8,191,1,0,0,0,10,193,1,0,0,0,12,216,1,0,0,0,14,231,1,0,0,
1666
- 0,16,236,1,0,0,0,18,238,1,0,0,0,20,240,1,0,0,0,22,242,1,0,0,0,24,249,1,0,
1667
- 0,0,26,252,1,0,0,0,28,272,1,0,0,0,30,286,1,0,0,0,32,288,1,0,0,0,34,290,1,
1668
- 0,0,0,36,308,1,0,0,0,38,316,1,0,0,0,40,324,1,0,0,0,42,332,1,0,0,0,44,345,
1659
+ 8,57,1,58,1,58,1,58,1,58,1,58,3,58,648,8,58,1,59,1,59,1,59,1,59,1,60,4,60,
1660
+ 655,8,60,11,60,12,60,656,1,60,0,1,110,61,0,2,4,6,8,10,12,14,16,18,20,22,
1661
+ 24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,
1662
+ 72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,
1663
+ 116,118,120,0,13,2,0,8,8,20,20,2,0,9,9,19,19,2,0,62,62,67,69,2,0,11,11,24,
1664
+ 24,2,0,62,62,67,67,1,0,25,27,1,0,23,24,1,0,19,22,1,0,17,18,1,0,63,64,1,0,
1665
+ 37,38,1,0,68,69,2,0,62,64,66,67,743,0,141,1,0,0,0,2,143,1,0,0,0,4,164,1,
1666
+ 0,0,0,6,166,1,0,0,0,8,191,1,0,0,0,10,193,1,0,0,0,12,216,1,0,0,0,14,231,1,
1667
+ 0,0,0,16,236,1,0,0,0,18,238,1,0,0,0,20,240,1,0,0,0,22,242,1,0,0,0,24,249,
1668
+ 1,0,0,0,26,252,1,0,0,0,28,272,1,0,0,0,30,286,1,0,0,0,32,288,1,0,0,0,34,290,
1669
+ 1,0,0,0,36,308,1,0,0,0,38,316,1,0,0,0,40,324,1,0,0,0,42,332,1,0,0,0,44,345,
1669
1670
  1,0,0,0,46,347,1,0,0,0,48,352,1,0,0,0,50,387,1,0,0,0,52,389,1,0,0,0,54,407,
1670
1671
  1,0,0,0,56,412,1,0,0,0,58,417,1,0,0,0,60,426,1,0,0,0,62,431,1,0,0,0,64,435,
1671
1672
  1,0,0,0,66,439,1,0,0,0,68,446,1,0,0,0,70,466,1,0,0,0,72,468,1,0,0,0,74,470,
@@ -1673,7 +1674,7 @@ const serializedATN = [4,1,81,658,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,
1673
1674
  1,0,0,0,86,509,1,0,0,0,88,514,1,0,0,0,90,517,1,0,0,0,92,527,1,0,0,0,94,530,
1674
1675
  1,0,0,0,96,536,1,0,0,0,98,539,1,0,0,0,100,549,1,0,0,0,102,554,1,0,0,0,104,
1675
1676
  560,1,0,0,0,106,564,1,0,0,0,108,570,1,0,0,0,110,597,1,0,0,0,112,633,1,0,
1676
- 0,0,114,635,1,0,0,0,116,646,1,0,0,0,118,648,1,0,0,0,120,653,1,0,0,0,122,
1677
+ 0,0,114,635,1,0,0,0,116,647,1,0,0,0,118,649,1,0,0,0,120,654,1,0,0,0,122,
1677
1678
  124,3,2,1,0,123,122,1,0,0,0,123,124,1,0,0,0,124,125,1,0,0,0,125,142,5,0,
1678
1679
  0,1,126,128,3,2,1,0,127,126,1,0,0,0,127,128,1,0,0,0,128,129,1,0,0,0,129,
1679
1680
  130,3,4,2,0,130,131,5,0,0,1,131,142,1,0,0,0,132,134,3,2,1,0,133,132,1,0,
@@ -1823,17 +1824,18 @@ const serializedATN = [4,1,81,658,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,
1823
1824
  0,633,627,1,0,0,0,633,628,1,0,0,0,633,629,1,0,0,0,633,630,1,0,0,0,633,631,
1824
1825
  1,0,0,0,633,632,1,0,0,0,634,113,1,0,0,0,635,637,5,33,0,0,636,638,3,116,58,
1825
1826
  0,637,636,1,0,0,0,637,638,1,0,0,0,638,640,1,0,0,0,639,641,5,34,0,0,640,639,
1826
- 1,0,0,0,640,641,1,0,0,0,641,115,1,0,0,0,642,647,3,112,56,0,643,647,3,110,
1827
- 55,0,644,647,3,118,59,0,645,647,3,120,60,0,646,642,1,0,0,0,646,643,1,0,0,
1828
- 0,646,644,1,0,0,0,646,645,1,0,0,0,647,117,1,0,0,0,648,649,7,4,0,0,649,650,
1829
- 5,55,0,0,650,651,7,4,0,0,651,119,1,0,0,0,652,654,7,12,0,0,653,652,1,0,0,
1830
- 0,654,655,1,0,0,0,655,653,1,0,0,0,655,656,1,0,0,0,656,121,1,0,0,0,103,123,
1831
- 127,133,136,141,145,148,152,154,158,160,164,168,174,178,180,185,188,191,
1832
- 196,199,202,206,209,212,216,225,229,231,236,249,254,258,261,266,270,272,
1833
- 279,284,286,303,308,312,316,320,324,328,332,337,340,345,350,359,364,369,
1834
- 372,378,381,384,387,392,397,400,404,407,412,422,426,431,437,441,446,454,
1835
- 459,464,466,480,486,489,498,502,507,512,521,525,532,543,547,552,558,562,
1836
- 566,572,575,586,597,620,622,633,637,640,646,655];
1827
+ 1,0,0,0,640,641,1,0,0,0,641,115,1,0,0,0,642,648,3,112,56,0,643,648,3,110,
1828
+ 55,0,644,648,3,118,59,0,645,648,3,120,60,0,646,648,3,24,12,0,647,642,1,0,
1829
+ 0,0,647,643,1,0,0,0,647,644,1,0,0,0,647,645,1,0,0,0,647,646,1,0,0,0,648,
1830
+ 117,1,0,0,0,649,650,7,4,0,0,650,651,5,55,0,0,651,652,7,4,0,0,652,119,1,0,
1831
+ 0,0,653,655,7,12,0,0,654,653,1,0,0,0,655,656,1,0,0,0,656,654,1,0,0,0,656,
1832
+ 657,1,0,0,0,657,121,1,0,0,0,103,123,127,133,136,141,145,148,152,154,158,
1833
+ 160,164,168,174,178,180,185,188,191,196,199,202,206,209,212,216,225,229,
1834
+ 231,236,249,254,258,261,266,270,272,279,284,286,303,308,312,316,320,324,
1835
+ 328,332,337,340,345,350,359,364,369,372,378,381,384,387,392,397,400,404,
1836
+ 407,412,422,426,431,437,441,446,454,459,464,466,480,486,489,498,502,507,
1837
+ 512,521,525,532,543,547,552,558,562,566,572,575,586,597,620,622,633,637,
1838
+ 640,647,656];
1837
1839
 
1838
1840
 
1839
1841
  const atn = new antlr4.atn.ATNDeserializer().deserialize(serializedATN);
@@ -4837,7 +4839,7 @@ class sequenceParser extends antlr4.Parser {
4837
4839
  let localctx = new ConditionContext(this, this._ctx, this.state);
4838
4840
  this.enterRule(localctx, 116, sequenceParser.RULE_condition);
4839
4841
  try {
4840
- this.state = 646;
4842
+ this.state = 647;
4841
4843
  this._errHandler.sync(this);
4842
4844
  var la_ = this._interp.adaptivePredict(this._input,101,this._ctx);
4843
4845
  switch(la_) {
@@ -4865,6 +4867,12 @@ class sequenceParser extends antlr4.Parser {
4865
4867
  this.textExpr();
4866
4868
  break;
4867
4869
 
4870
+ case 5:
4871
+ this.enterOuterAlt(localctx, 5);
4872
+ this.state = 646;
4873
+ this.emoji();
4874
+ break;
4875
+
4868
4876
  }
4869
4877
  } catch (re) {
4870
4878
  if(re instanceof antlr4.error.RecognitionException) {
@@ -4888,7 +4896,7 @@ class sequenceParser extends antlr4.Parser {
4888
4896
  var _la = 0; // Token type
4889
4897
  try {
4890
4898
  this.enterOuterAlt(localctx, 1);
4891
- this.state = 648;
4899
+ this.state = 649;
4892
4900
  _la = this._input.LA(1);
4893
4901
  if(!(_la===62 || _la===67)) {
4894
4902
  this._errHandler.recoverInline(this);
@@ -4897,9 +4905,9 @@ class sequenceParser extends antlr4.Parser {
4897
4905
  this._errHandler.reportMatch(this);
4898
4906
  this.consume();
4899
4907
  }
4900
- this.state = 649;
4901
- this.match(sequenceParser.IN);
4902
4908
  this.state = 650;
4909
+ this.match(sequenceParser.IN);
4910
+ this.state = 651;
4903
4911
  _la = this._input.LA(1);
4904
4912
  if(!(_la===62 || _la===67)) {
4905
4913
  this._errHandler.recoverInline(this);
@@ -4930,15 +4938,15 @@ class sequenceParser extends antlr4.Parser {
4930
4938
  var _la = 0; // Token type
4931
4939
  try {
4932
4940
  this.enterOuterAlt(localctx, 1);
4933
- this.state = 653;
4941
+ this.state = 654;
4934
4942
  this._errHandler.sync(this);
4935
4943
  var _alt = 1;
4936
4944
  do {
4937
4945
  switch (_alt) {
4938
4946
  case 1:
4939
- this.state = 652;
4947
+ this.state = 653;
4940
4948
  _la = this._input.LA(1);
4941
- if(!(((((_la - 62)) & ~0x1f) == 0 && ((1 << (_la - 62)) & 49) !== 0))) {
4949
+ if(!(((((_la - 62)) & ~0x1f) == 0 && ((1 << (_la - 62)) & 55) !== 0))) {
4942
4950
  this._errHandler.recoverInline(this);
4943
4951
  }
4944
4952
  else {
@@ -4949,7 +4957,7 @@ class sequenceParser extends antlr4.Parser {
4949
4957
  default:
4950
4958
  throw new antlr4.error.NoViableAltException(this);
4951
4959
  }
4952
- this.state = 655;
4960
+ this.state = 656;
4953
4961
  this._errHandler.sync(this);
4954
4962
  _alt = this._interp.adaptivePredict(this._input,102, this._ctx);
4955
4963
  } while ( _alt!=2 && _alt!=antlr4.atn.ATN.INVALID_ALT_NUMBER );
@@ -8555,6 +8563,10 @@ class ConditionContext extends antlr4.ParserRuleContext {
8555
8563
  return this.getTypedRuleContext(TextExprContext,0);
8556
8564
  };
8557
8565
 
8566
+ emoji() {
8567
+ return this.getTypedRuleContext(EmojiContext,0);
8568
+ };
8569
+
8558
8570
  enterRule(listener) {
8559
8571
  if(listener instanceof sequenceParserListener ) {
8560
8572
  listener.enterCondition(this);
@@ -8681,6 +8693,30 @@ class TextExprContext extends antlr4.ParserRuleContext {
8681
8693
  };
8682
8694
 
8683
8695
 
8696
+ INT = function(i) {
8697
+ if(i===undefined) {
8698
+ i = null;
8699
+ }
8700
+ if(i===null) {
8701
+ return this.getTokens(sequenceParser.INT);
8702
+ } else {
8703
+ return this.getToken(sequenceParser.INT, i);
8704
+ }
8705
+ };
8706
+
8707
+
8708
+ FLOAT = function(i) {
8709
+ if(i===undefined) {
8710
+ i = null;
8711
+ }
8712
+ if(i===null) {
8713
+ return this.getTokens(sequenceParser.FLOAT);
8714
+ } else {
8715
+ return this.getToken(sequenceParser.FLOAT, i);
8716
+ }
8717
+ };
8718
+
8719
+
8684
8720
  enterRule(listener) {
8685
8721
  if(listener instanceof sequenceParserListener ) {
8686
8722
  listener.enterTextExpr(this);
@@ -12667,7 +12703,7 @@ const coreIcons = {
12667
12703
  sagemaker: () => import('./assets/Arch_Amazon-SageMaker_16-DrLi5pZN.mjs')
12668
12704
  };
12669
12705
  const loadCloudIcon = async (key) => {
12670
- const { cloudIcons } = await import('./assets/CloudIcons-qEDx5IlS.mjs');
12706
+ const { cloudIcons } = await import('./assets/CloudIcons-De45wK6e.mjs');
12671
12707
  return cloudIcons[key] ?? null;
12672
12708
  };
12673
12709
  const loadIcon = async (iconKey) => {
@@ -12757,6 +12793,15 @@ function styleToAttr(style) {
12757
12793
  return Object.entries(style).map(([k, v]) => `${esc(k)}: ${esc(v)}`).join("; ");
12758
12794
  }
12759
12795
 
12796
+ function brightnessIgnoreAlpha(color) {
12797
+ const c = colorString.get.rgb(color) || [0, 0, 0];
12798
+ const [r, g, b] = c;
12799
+ return (r * 299 + g * 587 + b * 114) / 1e3;
12800
+ }
12801
+ function participantTextColor(color) {
12802
+ return brightnessIgnoreAlpha(color) > 128 ? "#000" : "#fff";
12803
+ }
12804
+
12760
12805
  const STROKE_WIDTH$1 = 1;
12761
12806
  const HALF_STROKE$1 = STROKE_WIDTH$1 / 2;
12762
12807
  const ICON_SIZE = 24;
@@ -12767,7 +12812,6 @@ const LABEL_HORIZONTAL_PADDING = 16;
12767
12812
  const STEREOTYPE_VERTICAL_OFFSET = 8;
12768
12813
  const STEREOTYPE_FONT_SIZE = 14;
12769
12814
  const BOUNDARY_ICON_VERTICAL_TWEAK = 2.75;
12770
- const PARTICIPANT_TEXT_FILL = "#222";
12771
12815
  function renderParticipant(p) {
12772
12816
  if (p.isStarter) return renderStarterParticipant(p);
12773
12817
  const rx = 4 - HALF_STROKE$1;
@@ -12802,7 +12846,7 @@ function renderParticipant(p) {
12802
12846
  const [, , vbW, vbH] = (icon.viewBox || "0 0 24 24").split(" ").map(Number);
12803
12847
  const scale = ICON_SIZE / Math.max(vbW, vbH);
12804
12848
  const iconAttrs = icon.attributes ? ` ${icon.attributes}` : "";
12805
- iconSvg = `<g class="participant-icon" transform="translate(${iconX}, ${iconY}) scale(${scale})"${iconAttrs}>
12849
+ iconSvg = `<g class="participant-icon" transform="translate(${iconX}, ${iconY}) scale(${scale})"${p.color ? ` style="color:${participantTextColor(normalizeHexColor(p.color))};"` : ""}${iconAttrs}>
12806
12850
  ${icon.content}
12807
12851
  </g>`;
12808
12852
  } else if (p.emoji) {
@@ -12868,12 +12912,9 @@ function colorAttrs(color) {
12868
12912
  const hex = normalizeHexColor(color);
12869
12913
  return {
12870
12914
  fillStyle: ` style="fill:${hex};"`,
12871
- textStyle: participantTextStyle()
12915
+ textStyle: ` style="fill:${participantTextColor(hex)};"`
12872
12916
  };
12873
12917
  }
12874
- function participantTextStyle() {
12875
- return ` style="fill:${PARTICIPANT_TEXT_FILL};"`;
12876
- }
12877
12918
 
12878
12919
  function renderLifeline(l) {
12879
12920
  const x = l.x + 0.5;
@@ -13510,7 +13551,6 @@ function buildThemeStyles(p) {
13510
13551
  .participant-label { font-family: ${p.fontFamily}; font-size: 14px; fill: ${p.participantText}; }
13511
13552
  .stereotype-label { fill: ${p.participantText}; }
13512
13553
  .participant-icon { color: ${p.participantText}; }
13513
- .participant-icon [fill="currentColor"]:not([stroke]) { stroke: ${p.participantBorder}; stroke-width: 1; }
13514
13554
  .lifeline { stroke: ${p.lifeline}; stroke-width: 1; }
13515
13555
  .message-line { stroke: ${p.messageArrow}; stroke-width: 1; shape-rendering: crispEdges; }
13516
13556
  .message-label { font-family: ${p.fontFamily}; font-size: 15px; fill: ${p.messageText}; }
@@ -13547,13 +13587,71 @@ function buildThemeStyles(p) {
13547
13587
  `;
13548
13588
  }
13549
13589
 
13590
+ const PALETTE_CSS_VARS = {
13591
+ canvasBg: "--color-bg-canvas",
13592
+ frameBg: "--color-bg-frame",
13593
+ frameBorder: "--color-border-frame",
13594
+ titleText: "--color-text-title",
13595
+ participantBg: "--color-bg-participant",
13596
+ participantBorder: "--color-border-participant",
13597
+ participantText: "--color-text-participant",
13598
+ messageArrow: "--color-message-arrow",
13599
+ messageText: "--color-text-message",
13600
+ lifeline: "--color-border-base",
13601
+ occurrenceBg: "--color-bg-occurrence",
13602
+ occurrenceBorder: "--color-border-occurrence",
13603
+ fragmentBorder: "--color-border-fragment",
13604
+ fragmentHeaderBg: "--color-bg-fragment-header",
13605
+ fragmentHeaderText: "--color-text-fragment-header",
13606
+ fragmentText: "--color-text-fragment",
13607
+ fragmentSeparator: "--color-border-fragment-separator",
13608
+ guardOpacity: "--opacity-guard",
13609
+ guardText: "--color-text-guard",
13610
+ commentText: "--color-text-comment",
13611
+ commentOpacity: "--opacity-comment",
13612
+ groupOutline: "--color-border-group",
13613
+ dividerLine: "--color-border-divider",
13614
+ dividerBg: "--color-bg-divider",
13615
+ dividerText: "--color-text-divider",
13616
+ numberText: "--color-text-number",
13617
+ fragmentNumberText: "--color-text-fragment-number",
13618
+ numberBg: "--color-bg-number"
13619
+ };
13620
+ function safeSvgPalette(palette) {
13621
+ if (!palette) return void 0;
13622
+ const safe = {};
13623
+ for (const [key, value] of Object.entries(palette)) {
13624
+ if (!(key in PALETTE_CSS_VARS) || typeof value !== "string") continue;
13625
+ if (key === "guardOpacity" || key === "commentOpacity") {
13626
+ const opacity = Number(value);
13627
+ if (Number.isFinite(opacity) && opacity >= 0 && opacity <= 1)
13628
+ safe[key] = value;
13629
+ } else if (/^[a-zA-Z0-9#(),.%/+\-\s]+$/.test(value) && !/\b(?:url|expression|var)\s*\(/i.test(value)) {
13630
+ safe[key] = value;
13631
+ }
13632
+ }
13633
+ return safe;
13634
+ }
13635
+ function normaliseBackingColor(color) {
13636
+ const rgb = colorString.get.rgb(color);
13637
+ if (!rgb || rgb[3] !== 1) {
13638
+ throw new TypeError("backingColor must be an opaque CSS colour");
13639
+ }
13640
+ return `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`;
13641
+ }
13642
+
13550
13643
  const FRAME_VIEWPORT_HEADER_ALLOWANCE = 28;
13551
13644
  const FRAME_BORDER_RADIUS = 4;
13552
13645
  function renderToSvg(code, options) {
13646
+ const safeOptions = options ? { ...options, palette: safeSvgPalette(options.palette) } : void 0;
13647
+ const surface = {
13648
+ backgroundColor: safeOptions?.palette?.canvasBg ?? "transparent"
13649
+ };
13553
13650
  const rootContext = RootContext(code);
13554
13651
  if (!rootContext) {
13555
13652
  return {
13556
13653
  svg: "<svg></svg>",
13654
+ surface,
13557
13655
  innerSvg: "",
13558
13656
  width: 0,
13559
13657
  height: 0,
@@ -13574,9 +13672,12 @@ function renderToSvg(code, options) {
13574
13672
  measureText: WidthProviderOnCanvas,
13575
13673
  enableNumbering: options?.enableNumbering
13576
13674
  });
13577
- return { ...composeSvg(geometry, options), geometry };
13675
+ return { ...composeSvg(geometry, safeOptions), geometry };
13578
13676
  }
13579
13677
  function composeSvg(g, options) {
13678
+ const surface = {
13679
+ backgroundColor: options?.palette?.canvasBg ?? "transparent"
13680
+ };
13580
13681
  const padding = 10;
13581
13682
  const headerH = FRAME_VIEWPORT_HEADER_ALLOWANCE;
13582
13683
  let contentLeftMargin = 1 + padding + g.frameBorderLeft;
@@ -13661,7 +13762,7 @@ function composeSvg(g, options) {
13661
13762
  const titleSvg = g.title ? `<text x="5" y="${headerLineDrawY / 2}" dominant-baseline="central" class="frame-title">${esc(g.title)}</text>` : "";
13662
13763
  const viewBox = `0 0 ${viewWidth} ${viewHeight}`;
13663
13764
  const defs = `<defs>
13664
- <style>${buildThemeStyles(resolvePalette(options?.theme))}</style>
13765
+ <style>${buildThemeStyles(resolveSvgPalette(options))}</style>
13665
13766
  </defs>`;
13666
13767
  const frame = `${frameSvg}
13667
13768
  ${headerLineSvg}
@@ -13669,13 +13770,35 @@ ${titleSvg}`;
13669
13770
  const content = `<g transform="translate(${contentLeftMargin}, ${headerLineY})">
13670
13771
  ${parts.join("\n")}
13671
13772
  </g>`;
13773
+ const backing = options?.backingColor !== void 0 ? `<rect class="export-backing" x="0" y="0" width="${viewWidth}" height="${viewHeight}" style="fill: ${normaliseBackingColor(options.backingColor)} !important"/>` : "";
13774
+ const background = surface.backgroundColor !== "transparent" ? `<rect class="canvas-background" x="0" y="0" width="${viewWidth}" height="${viewHeight}" fill="${surface.backgroundColor}"/>` : "";
13672
13775
  const innerSvg = `${defs}
13776
+ ${backing}
13777
+ ${background}
13673
13778
  ${frame}
13674
13779
  ${content}`;
13675
13780
  const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${viewWidth}" height="${viewHeight}" viewBox="${viewBox}">
13676
13781
  ${innerSvg}
13677
13782
  </svg>`;
13678
- return { svg, innerSvg, width: viewWidth, height: viewHeight, viewBox };
13783
+ return {
13784
+ svg,
13785
+ innerSvg,
13786
+ width: viewWidth,
13787
+ height: viewHeight,
13788
+ viewBox,
13789
+ surface
13790
+ };
13791
+ }
13792
+ function resolveSvgPalette(options) {
13793
+ const theme = resolvePalette(options?.theme);
13794
+ const palette = options?.palette;
13795
+ if (!palette) return theme;
13796
+ return {
13797
+ ...theme,
13798
+ ...palette,
13799
+ ...palette.fragmentHeaderBg ? { fragmentHeaderOpacity: "1" } : {},
13800
+ ...palette.numberBg ? { numberBgOpacity: "1" } : {}
13801
+ };
13679
13802
  }
13680
13803
 
13681
13804
  function readVersion() {