microboard-temp 0.6.5 → 0.7.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.
package/dist/esm/node.js CHANGED
@@ -1392,6 +1392,184 @@ var require_css_escape = __commonJS((exports, module) => {
1392
1392
  });
1393
1393
  });
1394
1394
 
1395
+ // src/Color/ColorValue.ts
1396
+ var SEMANTIC_COLOR_IDS = [
1397
+ "contrastNeutral",
1398
+ "contrastGray",
1399
+ "contrastRed",
1400
+ "contrastOrange",
1401
+ "contrastYellow",
1402
+ "contrastGreen",
1403
+ "contrastTeal",
1404
+ "contrastBlue",
1405
+ "contrastPurple",
1406
+ "contrastPink",
1407
+ "contrastBrown"
1408
+ ];
1409
+ var semanticColor = (id) => ({
1410
+ type: "semantic",
1411
+ id
1412
+ });
1413
+ var fixedColor = (value) => ({
1414
+ type: "fixed",
1415
+ value
1416
+ });
1417
+ function coerceColorValue(value) {
1418
+ if (typeof value === "string")
1419
+ return fixedColor(value);
1420
+ return value;
1421
+ }
1422
+ function coerceOptionalColorValue(value) {
1423
+ if (value === undefined)
1424
+ return;
1425
+ return coerceColorValue(value);
1426
+ }
1427
+ // src/Color/ContrastPalette.ts
1428
+ var CONTRAST_PALETTE = {
1429
+ contrastNeutral: {
1430
+ id: "contrastNeutral",
1431
+ label: "Neutral",
1432
+ light: "rgb(245, 246, 248)",
1433
+ dark: "rgb(20, 21, 26)",
1434
+ contrastRatio: 17.2
1435
+ },
1436
+ contrastGray: {
1437
+ id: "contrastGray",
1438
+ label: "Gray",
1439
+ light: "rgb(224, 225, 229)",
1440
+ dark: "rgb(55, 58, 70)",
1441
+ contrastRatio: 8.6
1442
+ },
1443
+ contrastRed: {
1444
+ id: "contrastRed",
1445
+ label: "Red",
1446
+ light: "rgb(255, 215, 210)",
1447
+ dark: "rgb(120, 10, 10)",
1448
+ contrastRatio: 8.5
1449
+ },
1450
+ contrastOrange: {
1451
+ id: "contrastOrange",
1452
+ label: "Orange",
1453
+ light: "rgb(255, 229, 195)",
1454
+ dark: "rgb(110, 44, 0)",
1455
+ contrastRatio: 8.4
1456
+ },
1457
+ contrastYellow: {
1458
+ id: "contrastYellow",
1459
+ label: "Yellow",
1460
+ light: "rgb(255, 249, 185)",
1461
+ dark: "rgb(89, 71, 0)",
1462
+ contrastRatio: 8.3
1463
+ },
1464
+ contrastGreen: {
1465
+ id: "contrastGreen",
1466
+ label: "Green",
1467
+ light: "rgb(193, 243, 179)",
1468
+ dark: "rgb(0, 74, 22)",
1469
+ contrastRatio: 8.4
1470
+ },
1471
+ contrastTeal: {
1472
+ id: "contrastTeal",
1473
+ label: "Teal",
1474
+ light: "rgb(176, 243, 240)",
1475
+ dark: "rgb(0, 68, 64)",
1476
+ contrastRatio: 8.8
1477
+ },
1478
+ contrastBlue: {
1479
+ id: "contrastBlue",
1480
+ label: "Blue",
1481
+ light: "rgb(208, 222, 255)",
1482
+ dark: "rgb(15, 42, 148)",
1483
+ contrastRatio: 8.7
1484
+ },
1485
+ contrastPurple: {
1486
+ id: "contrastPurple",
1487
+ label: "Purple",
1488
+ light: "rgb(232, 210, 255)",
1489
+ dark: "rgb(62, 0, 132)",
1490
+ contrastRatio: 9.8
1491
+ },
1492
+ contrastPink: {
1493
+ id: "contrastPink",
1494
+ label: "Pink",
1495
+ light: "rgb(255, 212, 228)",
1496
+ dark: "rgb(120, 0, 55)",
1497
+ contrastRatio: 8.5
1498
+ },
1499
+ contrastBrown: {
1500
+ id: "contrastBrown",
1501
+ label: "Brown",
1502
+ light: "rgb(242, 224, 200)",
1503
+ dark: "rgb(74, 33, 0)",
1504
+ contrastRatio: 10.7
1505
+ }
1506
+ };
1507
+ var CONTRAST_PALETTE_LIST = [
1508
+ CONTRAST_PALETTE.contrastNeutral,
1509
+ CONTRAST_PALETTE.contrastGray,
1510
+ CONTRAST_PALETTE.contrastRed,
1511
+ CONTRAST_PALETTE.contrastOrange,
1512
+ CONTRAST_PALETTE.contrastYellow,
1513
+ CONTRAST_PALETTE.contrastGreen,
1514
+ CONTRAST_PALETTE.contrastTeal,
1515
+ CONTRAST_PALETTE.contrastBlue,
1516
+ CONTRAST_PALETTE.contrastPurple,
1517
+ CONTRAST_PALETTE.contrastPink,
1518
+ CONTRAST_PALETTE.contrastBrown
1519
+ ];
1520
+ // src/Color/resolveColor.ts
1521
+ function resolveColor(value, theme, role) {
1522
+ if (typeof value === "string")
1523
+ return value;
1524
+ if (value.type === "fixed") {
1525
+ return value.value;
1526
+ }
1527
+ const pair = CONTRAST_PALETTE[value.id];
1528
+ const lightMode = theme === "light";
1529
+ if (role === "background") {
1530
+ return lightMode ? pair.light : pair.dark;
1531
+ } else {
1532
+ return lightMode ? pair.dark : pair.light;
1533
+ }
1534
+ }
1535
+ function resolvePairedForeground(background, theme) {
1536
+ if (background.type === "semantic") {
1537
+ return resolveColor(background, theme, "foreground");
1538
+ }
1539
+ return theme === "light" ? CONTRAST_PALETTE.contrastNeutral.dark : CONTRAST_PALETTE.contrastNeutral.light;
1540
+ }
1541
+ // src/Color/colorUtils.ts
1542
+ function srgbChannelToLinear(channel) {
1543
+ const c = channel / 255;
1544
+ return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
1545
+ }
1546
+ function relativeLuminance(r, g, b) {
1547
+ return 0.2126 * srgbChannelToLinear(r) + 0.7152 * srgbChannelToLinear(g) + 0.0722 * srgbChannelToLinear(b);
1548
+ }
1549
+ function contrastRatio(lum1, lum2) {
1550
+ const lighter = Math.max(lum1, lum2);
1551
+ const darker = Math.min(lum1, lum2);
1552
+ return (lighter + 0.05) / (darker + 0.05);
1553
+ }
1554
+ function meetsWCAG_AA(ratio) {
1555
+ return ratio >= 4.5;
1556
+ }
1557
+ function meetsWCAG_AAA(ratio) {
1558
+ return ratio >= 7;
1559
+ }
1560
+ function parseCssRgb(css) {
1561
+ const m = css.match(/rgba?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)/);
1562
+ if (!m)
1563
+ return null;
1564
+ return [parseFloat(m[1]), parseFloat(m[2]), parseFloat(m[3])];
1565
+ }
1566
+ function cssContrastRatio(css1, css2) {
1567
+ const rgb1 = parseCssRgb(css1);
1568
+ const rgb2 = parseCssRgb(css2);
1569
+ if (!rgb1 || !rgb2)
1570
+ return null;
1571
+ return contrastRatio(relativeLuminance(...rgb1), relativeLuminance(...rgb2));
1572
+ }
1395
1573
  // src/BoardCommand.ts
1396
1574
  class BoardCommand {
1397
1575
  board;
@@ -1985,12 +2163,14 @@ class Mbr {
1985
2163
  }
1986
2164
  render(context) {
1987
2165
  const { ctx } = context;
1988
- if (this.backgroundColor !== "none") {
1989
- ctx.fillStyle = this.backgroundColor;
2166
+ const resolvedBg = typeof this.backgroundColor === "string" ? this.backgroundColor : resolveColor(this.backgroundColor, "light", "background");
2167
+ const resolvedBorder = typeof this.borderColor === "string" ? this.borderColor : resolveColor(this.borderColor, "light", "foreground");
2168
+ if (resolvedBg !== "none") {
2169
+ ctx.fillStyle = resolvedBg;
1990
2170
  ctx.fillRect(this.left, this.top, this.getWidth(), this.getHeight());
1991
2171
  }
1992
2172
  if (this.strokeWidth) {
1993
- ctx.strokeStyle = this.borderColor;
2173
+ ctx.strokeStyle = resolvedBorder;
1994
2174
  ctx.lineWidth = this.strokeWidth;
1995
2175
  ctx.setLineDash([]);
1996
2176
  ctx.strokeRect(this.left, this.top, this.getWidth(), this.getHeight());
@@ -7549,6 +7729,7 @@ var conf = {
7549
7729
  plus: "Plus",
7550
7730
  plusAI: "PlusAI"
7551
7731
  },
7732
+ theme: "light",
7552
7733
  EVENTS_PUBLISH_INTERVAL: 100,
7553
7734
  EVENTS_RESEND_INTERVAL: 1000,
7554
7735
  SELECTION_COLOR: "rgb(71, 120, 245)",
@@ -39510,7 +39691,7 @@ class Connector2 extends BaseItem {
39510
39691
  this.endPointerStyle = endPointerStyle;
39511
39692
  this.transformation = new Transformation(this.id, this.board.events);
39512
39693
  this.linkTo = new LinkTo(this.id, this.board.events);
39513
- this.lineColor = lineColor ?? CONNECTOR_COLOR;
39694
+ this.lineColor = lineColor ?? fixedColor(CONNECTOR_COLOR);
39514
39695
  this.lineWidth = lineWidth ?? CONNECTOR_LINE_WIDTH;
39515
39696
  this.borderStyle = strokeStyle ?? CONNECTOR_BORDER_STYLE;
39516
39697
  this.text = new RichText(board, this.getMbr(), this.id, new Transformation, this.linkTo, conf.i18n.t("connector.textPlaceholder", {
@@ -40017,7 +40198,7 @@ class Connector2 extends BaseItem {
40017
40198
  div.style.transformOrigin = "left top";
40018
40199
  div.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scaleX}, ${scaleY})`;
40019
40200
  div.style.position = "absolute";
40020
- div.setAttribute("data-line-color", this.lineColor);
40201
+ div.setAttribute("data-line-color", resolveColor(this.lineColor, conf.theme, "foreground"));
40021
40202
  div.setAttribute("data-line-width", this.lineWidth.toString());
40022
40203
  div.setAttribute("data-line-style", this.lineStyle);
40023
40204
  div.setAttribute("data-border-style", this.borderStyle);
@@ -40113,7 +40294,9 @@ class Connector2 extends BaseItem {
40113
40294
  this.startPointerStyle = data.startPointerStyle ?? this.startPointerStyle;
40114
40295
  this.endPointerStyle = data.endPointerStyle ?? this.endPointerStyle;
40115
40296
  this.lineStyle = data.lineStyle ?? this.lineStyle;
40116
- this.lineColor = data.lineColor ?? this.lineColor;
40297
+ if (data.lineColor != null) {
40298
+ this.lineColor = coerceColorValue(data.lineColor);
40299
+ }
40117
40300
  this.lineWidth = data.lineWidth ?? this.lineWidth;
40118
40301
  this.borderStyle = data.borderStyle ?? this.borderStyle;
40119
40302
  if (data.transformation) {
@@ -40206,16 +40389,17 @@ class Connector2 extends BaseItem {
40206
40389
  const endPoint = this.endPoint;
40207
40390
  this.lines = getLine(this.lineStyle, startPoint, endPoint, this.middlePoint).addConnectedItemType(this.itemType);
40208
40391
  this.startPointer = getStartPointer(startPoint, this.startPointerStyle, this.lineStyle, this.lines, this.lineWidth * 0.1 + 0.2);
40209
- this.startPointer.path.setBorderColor(this.lineColor);
40392
+ const resolvedLineColor = resolveColor(this.lineColor, conf.theme, "foreground");
40393
+ this.startPointer.path.setBorderColor(resolvedLineColor);
40210
40394
  this.startPointer.path.setBorderWidth(this.lineWidth);
40211
- this.startPointer.path.setBackgroundColor(this.lineColor);
40395
+ this.startPointer.path.setBackgroundColor(resolvedLineColor);
40212
40396
  this.endPointer = getEndPointer(endPoint, this.endPointerStyle, this.lineStyle, this.lines, this.lineWidth * 0.1 + 0.2);
40213
- this.endPointer.path.setBorderColor(this.lineColor);
40397
+ this.endPointer.path.setBorderColor(resolvedLineColor);
40214
40398
  this.endPointer.path.setBorderWidth(this.lineWidth);
40215
- this.endPointer.path.setBackgroundColor(this.lineColor);
40399
+ this.endPointer.path.setBackgroundColor(resolvedLineColor);
40216
40400
  this.offsetLines();
40217
40401
  this.lines.setBorderWidth(this.lineWidth);
40218
- this.lines.setBorderColor(this.lineColor);
40402
+ this.lines.setBorderColor(resolvedLineColor);
40219
40403
  this.lines.setBorderStyle(this.borderStyle);
40220
40404
  this.updateTitle();
40221
40405
  }
@@ -40321,7 +40505,7 @@ class ConnectorData2 {
40321
40505
  startPointerStyle = "None";
40322
40506
  endPointerStyle = "ArrowThin";
40323
40507
  lineStyle = "straight";
40324
- lineColor = "";
40508
+ lineColor = fixedColor(CONNECTOR_COLOR);
40325
40509
  linkTo;
40326
40510
  lineWidth = 1;
40327
40511
  borderStyle = "solid";
@@ -41014,7 +41198,7 @@ class DefaultShapeData {
41014
41198
  text;
41015
41199
  linkTo;
41016
41200
  itemType = "Shape";
41017
- constructor(shapeType = "Rectangle", backgroundColor = "none", backgroundOpacity = 1, borderColor = conf.SHAPE_DEFAULT_STROKE_COLOR, borderOpacity = 1, borderStyle = "solid", borderWidth = 1, transformation = new DefaultTransformationData, text5 = new DefaultRichTextData, linkTo) {
41201
+ constructor(shapeType = "Rectangle", backgroundColor = fixedColor("none"), backgroundOpacity = 1, borderColor = fixedColor(conf.SHAPE_DEFAULT_STROKE_COLOR), borderOpacity = 1, borderStyle = "solid", borderWidth = 1, transformation = new DefaultTransformationData, text5 = new DefaultRichTextData, linkTo) {
41018
41202
  this.shapeType = shapeType;
41019
41203
  this.backgroundColor = backgroundColor;
41020
41204
  this.backgroundOpacity = backgroundOpacity;
@@ -41882,9 +42066,13 @@ class Shape extends BaseItem {
41882
42066
  if (data.linkTo) {
41883
42067
  this.linkTo.deserialize(data.linkTo);
41884
42068
  }
41885
- this.backgroundColor = data.backgroundColor ?? this.backgroundColor;
42069
+ if (data.backgroundColor != null) {
42070
+ this.backgroundColor = coerceColorValue(data.backgroundColor);
42071
+ }
41886
42072
  this.backgroundOpacity = data.backgroundOpacity ?? this.backgroundOpacity;
41887
- this.borderColor = data.borderColor ?? this.borderColor;
42073
+ if (data.borderColor != null) {
42074
+ this.borderColor = coerceColorValue(data.borderColor);
42075
+ }
41888
42076
  this.borderOpacity = data.borderOpacity ?? this.borderOpacity;
41889
42077
  this.borderStyle = data.borderStyle ?? this.borderStyle;
41890
42078
  this.borderWidth = data.borderWidth ?? this.borderWidth;
@@ -41991,7 +42179,7 @@ class Shape extends BaseItem {
41991
42179
  }
41992
42180
  applyBackgroundColor(backgroundColor) {
41993
42181
  this.backgroundColor = backgroundColor;
41994
- this.path.setBackgroundColor(backgroundColor);
42182
+ this.path.setBackgroundColor(resolveColor(backgroundColor, conf.theme, "background"));
41995
42183
  }
41996
42184
  setBackgroundColor(backgroundColor) {
41997
42185
  this.emit({
@@ -42027,7 +42215,7 @@ class Shape extends BaseItem {
42027
42215
  }
42028
42216
  applyBorderColor(borderColor) {
42029
42217
  this.borderColor = borderColor;
42030
- this.path.setBorderColor(borderColor);
42218
+ this.path.setBorderColor(resolveColor(borderColor, conf.theme, "foreground"));
42031
42219
  }
42032
42220
  setBorderColor(borderColor) {
42033
42221
  this.emit({
@@ -42286,7 +42474,7 @@ class StickerData {
42286
42474
  linkTo;
42287
42475
  text;
42288
42476
  itemType = "Sticker";
42289
- constructor(backgroundColor = stickerColors["Sky Blue"], transformation = new DefaultTransformationData, linkTo, text5 = new DefaultRichTextData([], "center", undefined)) {
42477
+ constructor(backgroundColor = fixedColor(stickerColors["Sky Blue"]), transformation = new DefaultTransformationData, linkTo, text5 = new DefaultRichTextData([], "center", undefined)) {
42290
42478
  this.backgroundColor = backgroundColor;
42291
42479
  this.transformation = transformation;
42292
42480
  this.linkTo = linkTo;
@@ -42398,7 +42586,9 @@ class Sticker extends BaseItem {
42398
42586
  };
42399
42587
  }
42400
42588
  deserialize(data) {
42401
- this.backgroundColor = data.backgroundColor ?? this.backgroundColor;
42589
+ if (data.backgroundColor != null) {
42590
+ this.backgroundColor = coerceColorValue(data.backgroundColor);
42591
+ }
42402
42592
  if (data.transformation) {
42403
42593
  this.transformation.deserialize(data.transformation);
42404
42594
  }
@@ -42422,7 +42612,7 @@ class Sticker extends BaseItem {
42422
42612
  this.stickerPath.transform(this.transformation.toMatrix());
42423
42613
  this.text.setContainer(this.textContainer.copy());
42424
42614
  this.textContainer.transform(this.transformation.toMatrix());
42425
- this.stickerPath.setBackgroundColor(this.backgroundColor);
42615
+ this.stickerPath.setBackgroundColor(resolveColor(this.backgroundColor, conf.theme, "background"));
42426
42616
  this.saveStickerData();
42427
42617
  }
42428
42618
  setId(id) {
@@ -42466,7 +42656,7 @@ class Sticker extends BaseItem {
42466
42656
  }
42467
42657
  applyBackgroundColor(backgroundColor) {
42468
42658
  this.backgroundColor = backgroundColor;
42469
- this.stickerPath.setBackgroundColor(backgroundColor);
42659
+ this.stickerPath.setBackgroundColor(resolveColor(backgroundColor, conf.theme, "background"));
42470
42660
  }
42471
42661
  setBackgroundColor(backgroundColor) {
42472
42662
  this.emit({
@@ -42529,7 +42719,7 @@ class Sticker extends BaseItem {
42529
42719
  const unscaledWidth = itemMbr.getWidth() / scaleX;
42530
42720
  const unscaledHeight = height2 / scaleY;
42531
42721
  div.id = this.getId();
42532
- div.style.backgroundColor = this.backgroundColor;
42722
+ div.style.backgroundColor = resolveColor(this.backgroundColor, conf.theme, "background");
42533
42723
  div.style.width = `${unscaledWidth}px`;
42534
42724
  div.style.height = `${unscaledHeight}px`;
42535
42725
  div.style.transformOrigin = "top left";
@@ -42803,7 +42993,7 @@ class DefaultFrameData {
42803
42993
  canChangeRatio;
42804
42994
  linkTo;
42805
42995
  itemType = "Frame";
42806
- constructor(shapeType = "Custom", backgroundColor = FRAME_FILL_COLOR, backgroundOpacity = 1, borderColor = FRAME_BORDER_COLOR, borderOpacity = 0.08, borderStyle = "solid", borderWidth = 0.2, transformation = new DefaultTransformationData, children = [], text5 = new DefaultRichTextData([], "top", 600), canChangeRatio = true, linkTo) {
42996
+ constructor(shapeType = "Custom", backgroundColor = fixedColor(FRAME_FILL_COLOR), backgroundOpacity = 1, borderColor = fixedColor(FRAME_BORDER_COLOR), borderOpacity = 0.08, borderStyle = "solid", borderWidth = 0.2, transformation = new DefaultTransformationData, children = [], text5 = new DefaultRichTextData([], "top", 600), canChangeRatio = true, linkTo) {
42807
42997
  this.shapeType = shapeType;
42808
42998
  this.backgroundColor = backgroundColor;
42809
42999
  this.backgroundOpacity = backgroundOpacity;
@@ -43011,9 +43201,13 @@ class Frame2 extends BaseItem {
43011
43201
  this.initPath();
43012
43202
  }
43013
43203
  this.linkTo.deserialize(data.linkTo);
43014
- this.backgroundColor = data.backgroundColor ?? this.backgroundColor;
43204
+ if (data.backgroundColor != null) {
43205
+ this.backgroundColor = coerceColorValue(data.backgroundColor);
43206
+ }
43015
43207
  this.backgroundOpacity = data.backgroundOpacity ?? this.backgroundOpacity;
43016
- this.borderColor = data.borderColor ?? this.borderColor;
43208
+ if (data.borderColor != null) {
43209
+ this.borderColor = coerceColorValue(data.borderColor);
43210
+ }
43017
43211
  this.borderOpacity = data.borderOpacity ?? this.borderOpacity;
43018
43212
  this.borderStyle = data.borderStyle ?? this.borderStyle;
43019
43213
  this.borderWidth = data.borderWidth ?? this.borderWidth;
@@ -43051,9 +43245,9 @@ class Frame2 extends BaseItem {
43051
43245
  this.path.transform(this.transformation.toMatrix());
43052
43246
  this.textContainer.transform(this.transformation.toMatrix());
43053
43247
  }
43054
- this.path.setBackgroundColor(this.backgroundColor);
43248
+ this.path.setBackgroundColor(resolveColor(this.backgroundColor, conf.theme, "background"));
43055
43249
  this.path.setBackgroundOpacity(this.backgroundOpacity);
43056
- this.path.setBorderColor(this.borderColor);
43250
+ this.path.setBorderColor(resolveColor(this.borderColor, conf.theme, "foreground"));
43057
43251
  this.path.setBorderWidth(this.borderWidth);
43058
43252
  this.path.setBorderStyle(this.borderStyle);
43059
43253
  this.path.setBorderOpacity(this.borderOpacity);
@@ -43204,7 +43398,7 @@ class Frame2 extends BaseItem {
43204
43398
  }
43205
43399
  applyBackgroundColor(backgroundColor) {
43206
43400
  this.backgroundColor = backgroundColor;
43207
- this.path.setBackgroundColor(backgroundColor);
43401
+ this.path.setBackgroundColor(resolveColor(backgroundColor, conf.theme, "background"));
43208
43402
  }
43209
43403
  setBackgroundColor(backgroundColor) {
43210
43404
  this.emit({
@@ -43276,9 +43470,9 @@ class Frame2 extends BaseItem {
43276
43470
  renderHTML(documentFactory) {
43277
43471
  const div = documentFactory.createElement("frame-item");
43278
43472
  div.id = this.getId();
43279
- div.style.backgroundColor = this.backgroundColor;
43473
+ div.style.backgroundColor = resolveColor(this.backgroundColor, conf.theme, "background");
43280
43474
  div.style.opacity = this.backgroundOpacity.toString();
43281
- div.style.borderColor = this.borderColor;
43475
+ div.style.borderColor = resolveColor(this.borderColor, conf.theme, "foreground");
43282
43476
  div.style.borderWidth = `${this.borderWidth}px`;
43283
43477
  div.style.borderStyle = this.borderStyle;
43284
43478
  const { translateX, translateY, scaleX, scaleY } = this.transformation.getMatrixData();
@@ -44914,6 +45108,7 @@ class Drawing extends BaseItem {
44914
45108
  lines = [];
44915
45109
  linkTo;
44916
45110
  strokeWidth = 1;
45111
+ borderColor = fixedColor(conf.PEN_DEFAULT_COLOR);
44917
45112
  borderStyle = "solid";
44918
45113
  linePattern = scalePatterns(this.strokeWidth)[this.borderStyle];
44919
45114
  borderOpacity = 1;
@@ -44959,7 +45154,7 @@ class Drawing extends BaseItem {
44959
45154
  this.linkTo.deserialize(data.linkTo);
44960
45155
  this.optimizePoints();
44961
45156
  this.transformation.deserialize(data.transformation);
44962
- this.borderColor = data.strokeStyle;
45157
+ this.borderColor = coerceColorValue(data.strokeStyle);
44963
45158
  this.strokeWidth = data.strokeWidth;
44964
45159
  this.updateGeometry();
44965
45160
  return this;
@@ -45067,7 +45262,7 @@ class Drawing extends BaseItem {
45067
45262
  }
45068
45263
  const ctx = context.ctx;
45069
45264
  ctx.save();
45070
- ctx.strokeStyle = this.borderColor;
45265
+ ctx.strokeStyle = resolveColor(this.borderColor, conf.theme, "foreground");
45071
45266
  ctx.lineWidth = this.strokeWidth;
45072
45267
  ctx.lineCap = "round";
45073
45268
  ctx.setLineDash(this.linePattern);
@@ -45094,7 +45289,7 @@ class Drawing extends BaseItem {
45094
45289
  svg3.setAttribute("style", "position: absolute; overflow: visible;");
45095
45290
  const pathElement = documentFactory.createElementNS("http://www.w3.org/2000/svg", "path");
45096
45291
  pathElement.setAttribute("d", this.getPathData());
45097
- pathElement.setAttribute("stroke", this.borderColor);
45292
+ pathElement.setAttribute("stroke", resolveColor(this.borderColor, conf.theme, "foreground"));
45098
45293
  pathElement.setAttribute("stroke-width", `${this.strokeWidth}`);
45099
45294
  pathElement.setAttribute("fill", "none");
45100
45295
  svg3.appendChild(pathElement);
@@ -45187,7 +45382,7 @@ class Drawing extends BaseItem {
45187
45382
  case "Drawing":
45188
45383
  switch (op.method) {
45189
45384
  case "setStrokeColor":
45190
- this.borderColor = op.color;
45385
+ this.borderColor = coerceColorValue(op.color);
45191
45386
  break;
45192
45387
  case "setStrokeWidth":
45193
45388
  this.strokeWidth = op.width;
@@ -59740,12 +59935,17 @@ export {
59740
59935
  tempStorage,
59741
59936
  tagByType,
59742
59937
  stickerColors,
59938
+ srgbChannelToLinear,
59743
59939
  sha256,
59940
+ semanticColor,
59744
59941
  scalePatterns,
59745
59942
  scaleElementBy,
59746
59943
  rgbToRgba,
59944
+ resolvePairedForeground,
59945
+ resolveColor,
59747
59946
  resizeAndConvertToPng,
59748
59947
  resetElementScale,
59948
+ relativeLuminance,
59749
59949
  registerItem,
59750
59950
  quickAddItem,
59751
59951
  prepareVideo,
@@ -59754,8 +59954,11 @@ export {
59754
59954
  positionRelatively,
59755
59955
  positionAbsolutely,
59756
59956
  parsersHTML,
59957
+ parseCssRgb,
59757
59958
  omitDefaultProperties,
59758
59959
  messageRouter,
59960
+ meetsWCAG_AAA,
59961
+ meetsWCAG_AA,
59759
59962
  itemValidators,
59760
59963
  itemFactories,
59761
59964
  isShallowSimilarTo,
@@ -59782,14 +59985,19 @@ export {
59782
59985
  getControlPointData,
59783
59986
  getBlobFromDataURL,
59784
59987
  forceNumberIntoInterval,
59988
+ fixedColor,
59785
59989
  fileTosha256,
59786
59990
  exportBoardSnapshot,
59787
59991
  editModeHotkeyRegistry,
59788
59992
  decodeHtml,
59789
59993
  defaultCursors as cursors,
59994
+ cssContrastRatio,
59790
59995
  createVideoItem,
59791
59996
  createEvents,
59997
+ contrastRatio,
59792
59998
  conf,
59999
+ coerceOptionalColorValue,
60000
+ coerceColorValue,
59793
60001
  checkHotkeys,
59794
60002
  catmullRomInterpolate,
59795
60003
  captureFrame,
@@ -59815,6 +60023,7 @@ export {
59815
60023
  STEP_STROKE_WIDTH,
59816
60024
  SHAPE_LAST_TYPE_KEY,
59817
60025
  SHAPES_CATEGORIES,
60026
+ SEMANTIC_COLOR_IDS,
59818
60027
  RichText,
59819
60028
  QuadraticBezier,
59820
60029
  Presence,
@@ -59866,6 +60075,8 @@ export {
59866
60075
  Camera,
59867
60076
  CURSORS_IDLE_CLEANUP_DELAY,
59868
60077
  CURSORS_ANIMATION_DURATION,
60078
+ CONTRAST_PALETTE_LIST,
60079
+ CONTRAST_PALETTE,
59869
60080
  CONTEXT_NODE_HIGHLIGHT_COLOR,
59870
60081
  CONNECTOR_POINTER_TYPES,
59871
60082
  BoardSelection,
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The two display modes. Items using semantic colors automatically adapt
3
+ * their light/dark variants based on this setting.
4
+ */
5
+ export type Theme = 'light' | 'dark';
6
+ /**
7
+ * Which role a color plays in a light/dark pair.
8
+ * - 'background' → the item's fill / container surface
9
+ * - 'foreground' → text, strokes, icons drawn on top of the background
10
+ */
11
+ export type ColorRole = 'background' | 'foreground';
12
+ /**
13
+ * All semantic color identifiers. Each ID maps to a ContrastPair in the
14
+ * palette — a light variant and a dark variant that together achieve ≥4.5:1
15
+ * WCAG AA contrast when used as a background/foreground pair.
16
+ */
17
+ export declare const SEMANTIC_COLOR_IDS: readonly ["contrastNeutral", "contrastGray", "contrastRed", "contrastOrange", "contrastYellow", "contrastGreen", "contrastTeal", "contrastBlue", "contrastPurple", "contrastPink", "contrastBrown"];
18
+ export type SemanticColorId = (typeof SEMANTIC_COLOR_IDS)[number];
19
+ /**
20
+ * A theme-aware color that resolves to its light or dark variant at render
21
+ * time based on the active Theme. Using semantic colors guarantees ≥4.5:1
22
+ * contrast between any background/foreground pair from the same ID.
23
+ */
24
+ export interface SemanticColor {
25
+ readonly type: 'semantic';
26
+ readonly id: SemanticColorId;
27
+ }
28
+ /**
29
+ * A literal CSS color string (`rgb(…)`, `#rrggbb`, `rgba(…)`, `"none"`).
30
+ * Fixed colors do not react to theme changes — they preserve user intent for
31
+ * branding or artistic purposes.
32
+ */
33
+ export interface FixedColor {
34
+ readonly type: 'fixed';
35
+ readonly value: string;
36
+ }
37
+ /**
38
+ * The unified color value type used by all item data interfaces.
39
+ * Replaces the previous bare `string` color fields.
40
+ */
41
+ export type ColorValue = SemanticColor | FixedColor;
42
+ export declare const semanticColor: (id: SemanticColorId) => SemanticColor;
43
+ export declare const fixedColor: (value: string) => FixedColor;
44
+ /**
45
+ * Coerce a legacy serialized string or an already-typed ColorValue into a
46
+ * ColorValue. Strings become FixedColor so that existing boards load without
47
+ * modification.
48
+ */
49
+ export declare function coerceColorValue(value: string | ColorValue): ColorValue;
50
+ /**
51
+ * Coerce a legacy serialized string or ColorValue, allowing undefined
52
+ * (returns undefined when input is undefined).
53
+ */
54
+ export declare function coerceOptionalColorValue(value: string | ColorValue | undefined): ColorValue | undefined;
@@ -0,0 +1,45 @@
1
+ import { SemanticColorId } from './ColorValue';
2
+ /**
3
+ * A precomputed pair of sRGB colours for a single semantic ID.
4
+ *
5
+ * Light/dark mode rendering rules:
6
+ * - Light mode → `light` = background surface, `dark` = foreground/text
7
+ * - Dark mode → `dark` = background surface, `light` = foreground/text
8
+ *
9
+ * Every pair has been verified to meet WCAG 2.1 AAA (≥7:1 contrast ratio)
10
+ * between its `light` and `dark` variants.
11
+ *
12
+ * CIELAB hue coordinates (a*, b*) are kept consistent between variants so
13
+ * that the perceptual hue identity is preserved across themes.
14
+ */
15
+ export interface ContrastPair {
16
+ readonly id: SemanticColorId;
17
+ /** Human-readable label for palette UIs. */
18
+ readonly label: string;
19
+ /** Light variant — high lightness, used as background in light mode. */
20
+ readonly light: string;
21
+ /** Dark variant — low lightness, used as background in dark mode. */
22
+ readonly dark: string;
23
+ /**
24
+ * Pre-verified WCAG contrast ratio between `light` and `dark`.
25
+ * All values are ≥7.0 (AAA). Stored for fast assertion in tests and
26
+ * for display in accessibility auditing UIs.
27
+ */
28
+ readonly contrastRatio: number;
29
+ }
30
+ /**
31
+ * The canonical semantic colour palette.
32
+ *
33
+ * Values were derived from CIELAB (preserving a*, b* per hue family while
34
+ * varying L* to ~90 for light and ~22 for dark) and converted to sRGB.
35
+ * Each pair's contrast ratio was computed via the WCAG relative-luminance
36
+ * formula and rounded down to one decimal place.
37
+ *
38
+ * Yellow/orange dark variants appear brownish — this is intentional. The
39
+ * yellow family cannot produce a dark counterpart that is both accessible
40
+ * (≥4.5:1) *and* looks saturated yellow; a warm brown is the closest
41
+ * perceptually coherent accessible dark.
42
+ */
43
+ export declare const CONTRAST_PALETTE: Record<SemanticColorId, ContrastPair>;
44
+ /** Ordered list of semantic colour pairs for display in palette UIs. */
45
+ export declare const CONTRAST_PALETTE_LIST: ContrastPair[];
@@ -0,0 +1,32 @@
1
+ /**
2
+ * WCAG 2.1 color-contrast utilities.
3
+ *
4
+ * All calculations follow the W3C specification:
5
+ * https://www.w3.org/TR/WCAG21/#dfn-relative-luminance
6
+ */
7
+ /** Convert a single sRGB channel value (0–255) to linear light. */
8
+ export declare function srgbChannelToLinear(channel: number): number;
9
+ /**
10
+ * Relative luminance of an sRGB colour (channels 0–255).
11
+ * Returns a value in [0, 1] where 0 = black, 1 = white.
12
+ */
13
+ export declare function relativeLuminance(r: number, g: number, b: number): number;
14
+ /**
15
+ * WCAG contrast ratio between two relative luminance values.
16
+ * Returns a value ≥1 (1 = no contrast, 21 = black on white).
17
+ */
18
+ export declare function contrastRatio(lum1: number, lum2: number): number;
19
+ /** Returns true if the contrast ratio meets WCAG 2.1 AA (≥4.5:1). */
20
+ export declare function meetsWCAG_AA(ratio: number): boolean;
21
+ /** Returns true if the contrast ratio meets WCAG 2.1 AAA (≥7:1). */
22
+ export declare function meetsWCAG_AAA(ratio: number): boolean;
23
+ /**
24
+ * Parse an `rgb(r, g, b)` or `rgba(r, g, b, a)` CSS string.
25
+ * Returns `[r, g, b]` channels in [0, 255], or `null` if unparseable.
26
+ */
27
+ export declare function parseCssRgb(css: string): [number, number, number] | null;
28
+ /**
29
+ * Compute the WCAG contrast ratio between two CSS `rgb(…)` strings.
30
+ * Returns `null` if either string cannot be parsed.
31
+ */
32
+ export declare function cssContrastRatio(css1: string, css2: string): number | null;
@@ -0,0 +1,6 @@
1
+ export type { Theme, ColorRole, SemanticColorId, SemanticColor, FixedColor, ColorValue } from './ColorValue';
2
+ export { SEMANTIC_COLOR_IDS, semanticColor, fixedColor, coerceColorValue, coerceOptionalColorValue } from './ColorValue';
3
+ export type { ContrastPair } from './ContrastPalette';
4
+ export { CONTRAST_PALETTE, CONTRAST_PALETTE_LIST } from './ContrastPalette';
5
+ export { resolveColor, resolvePairedForeground } from './resolveColor';
6
+ export { srgbChannelToLinear, relativeLuminance, contrastRatio, meetsWCAG_AA, meetsWCAG_AAA, parseCssRgb, cssContrastRatio, } from './colorUtils';