@cardenelabs/cdl 0.63.0 → 0.64.1

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/CHANGELOG.md CHANGED
@@ -5,6 +5,55 @@ CDL (Chainome Diagram Language) の主要変更履歴。
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.64.1] - 2026-09-15
9
+
10
+ ### Fixed
11
+
12
+ - **別の段に置いた lane の枠線を、説明文が跨いだと指摘しないようにした** (#863)
13
+
14
+ 検証器の軸 64 (`lane-border-clearance`) は説明文の箱と lane の左右の枠線を横の範囲だけで比べ、全ての lane を
15
+ 「図の上から下まで続く列」 として扱っていた。 縦の範囲を持つ lane の真下や真上に別の段があると、その段の
16
+ 説明文が枠線の延長上に来ただけで指摘していた。 枠線はそこに描かれていないので、見た目は何も跨いでいない。
17
+
18
+ 縦の範囲を持つ lane は、説明文と縦に重なる時だけ比べる。
19
+
20
+ | lane | 縦の範囲 | 比べ方 |
21
+ |---|---|---|
22
+ | `contain: true` | 中の node を囲む枠 (`y` / `height`) | 枠と縦に重なる説明文だけ |
23
+ | `posX` / `posY` / `posH` を揃えた lane | 置いた `posY` から `posH` まで | 同上 |
24
+ | 上の 2 つ以外 | 持たない (`height` は名札の帯の高さ) | 変更前と同じく上下いっぱいの列 |
25
+
26
+ 範囲を持つ lane でも、中の node が範囲の外にあれば その node の上下まで範囲を広げる (高さを指定した lane に
27
+ 座標を持たない node を入れると、node は段の位置に並んで範囲の外に出るため)。
28
+
29
+ 判定は `rectCrossesLaneBorder` (`layout/spec.ts`) に 1 本化し、engine が説明文の置き場所を選ぶ 2 か所
30
+ (左への迂回 `leftRouteIsBlocked` と説明文の反転 `flipLabelToFreeSide`) も同じ関数を呼ぶ。 これまでは
31
+ 「検証器と同一規約」 と書きながら別々に実装しており、検証器だけを直すと engine が別の段の枠線を避け続けて
32
+ 空いている側へ説明文を移さなくなる。
33
+
34
+ dragon のカタログ 502 図で配置 (edge の経路と説明文の位置) は 1 図も変わらない。 効くのは部品のように
35
+ `posX` / `posY` / `posH` で段に分けて置いた図で、dragon は部品 80 種を 2 段に並べた時の指摘が消える
36
+ (dragon#1993)。
37
+
38
+ ## [0.64.0] - 2026-09-14
39
+
40
+ ### Fixed
41
+
42
+ - **札に書いた色の出どころ (`colorSource`) の状態で札の色を引くようにした** (#861)
43
+
44
+ 札 (`badge`) の型は `colorSource` を持ち「別 signal で色を切替可能」 と書いていたが、札を描く部品は 1 度も
45
+ 読まず、色は札の字 (`source` の値) でしか引いていなかった。 `colorSource` を書くと、その状態の値で `map` を
46
+ 引いた色で塗る。 札の字は今どおり `source` の値を描く。
47
+
48
+ | 書き方 | 色を引く鍵 |
49
+ |---|---|
50
+ | `colorSource` を書かない | `source` の値 (変更前と同じ) |
51
+ | `colorSource` を書き、その状態がある | `colorSource` の状態の値 |
52
+ | `colorSource` を書き、その状態が無い | 引かない (色を付けない)。 `source` の値に戻すと書き間違いが効いているように見える |
53
+
54
+ 読み出しの型 (`CdlReadout`) の 107 種の欄を、描く部品 (`ReadoutWidget`) が読むかを構文木で突き合わせる
55
+ 検査を足した。 読まない欄は `colorSource` の 1 つだけだった。
56
+
8
57
  ## [0.63.0] - 2026-09-13
9
58
 
10
59
  ### Changed
@@ -2678,7 +2727,8 @@ Initial OSS release.
2678
2727
  使わない = annotated tag を push しても GitHub Release は作られず、 Release を作っていない版で
2679
2728
  行き止まりになる。
2680
2729
 
2681
- [Unreleased]: https://github.com/cardene777/cdl/compare/v0.63.0...HEAD
2730
+ [Unreleased]: https://github.com/cardene777/cdl/compare/v0.64.0...HEAD
2731
+ [0.64.0]: https://github.com/cardene777/cdl/compare/v0.63.0...v0.64.0
2682
2732
  [0.63.0]: https://github.com/cardene777/cdl/compare/v0.62.0...v0.63.0
2683
2733
  [0.62.0]: https://github.com/cardene777/cdl/compare/v0.61.0...v0.62.0
2684
2734
  [0.61.0]: https://github.com/cardene777/cdl/compare/v0.60.0...v0.61.0
package/dist/index.cjs CHANGED
@@ -2697,16 +2697,12 @@ function ReadoutWidget({
2697
2697
  colors: readout.colors ?? ["#e0e6ef", "#2d6a8f"]
2698
2698
  }
2699
2699
  );
2700
- case "badge":
2701
- return /* @__PURE__ */ jsxRuntime.jsx(
2702
- BadgeReadout,
2703
- {
2704
- id: readout.id,
2705
- label,
2706
- value: typeof rawValue === "string" ? rawValue : String(rawValue ?? ""),
2707
- map: readout.map
2708
- }
2709
- );
2700
+ case "badge": {
2701
+ const value = typeof rawValue === "string" ? rawValue : String(rawValue ?? "");
2702
+ const colorRaw = readout.colorSource === void 0 ? void 0 : stateOverrides[readout.colorSource];
2703
+ const colorKey = readout.colorSource === void 0 ? value : colorRaw === void 0 ? void 0 : String(colorRaw);
2704
+ return /* @__PURE__ */ jsxRuntime.jsx(BadgeReadout, { id: readout.id, label, value, colorKey, map: readout.map });
2705
+ }
2710
2706
  case "status-dot":
2711
2707
  return /* @__PURE__ */ jsxRuntime.jsx(
2712
2708
  StatusDotReadout,
@@ -4428,9 +4424,10 @@ function BadgeReadout({
4428
4424
  id,
4429
4425
  label,
4430
4426
  value,
4427
+ colorKey,
4431
4428
  map
4432
4429
  }) {
4433
- const entry = map?.find((m) => m.value === value);
4430
+ const entry = colorKey === void 0 ? void 0 : map?.find((m) => m.value === colorKey);
4434
4431
  const color = entry?.color;
4435
4432
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cdl-ip-item cdl-ip-readout-badge", "data-cdl-readout": id, children: [
4436
4433
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "cdl-ip-label", children: label }),
@@ -12560,6 +12557,25 @@ var ARROW_ENDPOINT_CENTER_TOL = 8;
12560
12557
  var ROW_GAP_VARIANCE_TOL = 40;
12561
12558
  var COLUMN_GAP_VARIANCE_TOL = 40;
12562
12559
  var LANE_BORDER_CLEARANCE_TOL = 2;
12560
+ function laneVerticalRange(lane, nodes) {
12561
+ const pinnedWithHeight = lane.posX !== void 0 && lane.posY !== void 0 && lane.posH !== void 0;
12562
+ if (!lane.contain && !pinnedWithHeight) return void 0;
12563
+ let top = lane.y;
12564
+ let bottom = lane.y + lane.height;
12565
+ for (const n of nodes) {
12566
+ if (n.lane !== lane.id) continue;
12567
+ top = Math.min(top, n.cy - n.h / 2);
12568
+ bottom = Math.max(bottom, n.cy + n.h / 2);
12569
+ }
12570
+ return { top, bottom };
12571
+ }
12572
+ function rectCrossesLaneBorder(rect, lane, range) {
12573
+ if (range !== void 0 && (rect.y + rect.h <= range.top || rect.y >= range.bottom)) return void 0;
12574
+ const crosses = (lineX) => rect.x < lineX - LANE_BORDER_CLEARANCE_TOL && rect.x + rect.w > lineX + LANE_BORDER_CLEARANCE_TOL;
12575
+ if (crosses(lane.x)) return "left";
12576
+ if (crosses(lane.x + lane.width)) return "right";
12577
+ return void 0;
12578
+ }
12563
12579
  var NEAR_COLLISION_POLICY = {
12564
12580
  "node|node": 70,
12565
12581
  "node|edge-label": 32,
@@ -18151,6 +18167,7 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes, \u5
18151
18167
  }
18152
18168
  }
18153
18169
  {
18170
+ const rangeByLane = new Map(laid.lanes.map((lane) => [lane.id, laneVerticalRange(lane, laid.nodes)]));
18154
18171
  for (const e of laid.edges) {
18155
18172
  if (!hasRenderedLabel(e)) continue;
18156
18173
  const fromNode = nodeById.get(e.from);
@@ -18159,21 +18176,14 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes, \u5
18159
18176
  const allowedLanes = /* @__PURE__ */ new Set();
18160
18177
  if (fromNode) allowedLanes.add(fromNode.lane);
18161
18178
  if (toNode) allowedLanes.add(toNode.lane);
18162
- const boxW = computeLabelBoxW(e.label, e.sub);
18163
- const anchor = e.labelAnchor;
18164
- const boxLeftX = anchor === "start" ? 0 : anchor === "end" ? -boxW : -boxW / 2;
18165
- const bx = e.labelX + boxLeftX;
18166
- const bxRight = bx + boxW;
18179
+ const box = computeLabelBBoxWorld(e);
18167
18180
  for (const lane of laid.lanes) {
18168
18181
  if (allowedLanes.has(lane.id)) continue;
18169
- const laneLeft = lane.x;
18170
- const laneRight = lane.x + lane.width;
18171
- const crossesLeft = bx < laneLeft - LANE_BORDER_CLEARANCE_TOL && bxRight > laneLeft + LANE_BORDER_CLEARANCE_TOL;
18172
- const crossesRight = bx < laneRight - LANE_BORDER_CLEARANCE_TOL && bxRight > laneRight + LANE_BORDER_CLEARANCE_TOL;
18173
- if (crossesLeft || crossesRight) {
18182
+ const side = rectCrossesLaneBorder(box, lane, rangeByLane.get(lane.id));
18183
+ if (side !== void 0) {
18174
18184
  push(
18175
18185
  "lane-border-clearance",
18176
- `edge "${e.id}" label bbox (${bx.toFixed(0)}..${bxRight.toFixed(0)}) \u304C\u975E\u767A\u7740 lane "${lane.id}" (${laneLeft.toFixed(0)}..${laneRight.toFixed(0)}) \u306E ${crossesLeft ? "\u5DE6" : "\u53F3"} border \u3092\u8CAB\u901A`
18186
+ `edge "${e.id}" label bbox (${box.x.toFixed(0)}..${(box.x + box.w).toFixed(0)}) \u304C\u975E\u767A\u7740 lane "${lane.id}" (${lane.x.toFixed(0)}..${(lane.x + lane.width).toFixed(0)}) \u306E ${side === "left" ? "\u5DE6" : "\u53F3"} border \u3092\u8CAB\u901A`
18177
18187
  );
18178
18188
  break;
18179
18189
  }
@@ -18796,6 +18806,7 @@ function layoutEdges(diag, nodes, lanes = []) {
18796
18806
  const labelMarginExtra = (diag.viewport?.labelMargin ?? diag.viewport?.gap ?? DEFAULT_LABEL_MARGIN) - DEFAULT_LABEL_MARGIN;
18797
18807
  const nmap = new Map(nodes.map((n) => [n.id, n]));
18798
18808
  const lmap = new Map(lanes.map((l) => [l.id, l]));
18809
+ const laneRangeById = new Map(lanes.map((l) => [l.id, laneVerticalRange(l, nodes)]));
18799
18810
  const positionedEdges = [];
18800
18811
  const autoEdges = [];
18801
18812
  const selfLoopEdges = [];
@@ -19020,7 +19031,7 @@ function layoutEdges(diag, nodes, lanes = []) {
19020
19031
  nodes.filter((n) => n.id === c.from || n.id === c.to).map((n) => n.lane)
19021
19032
  );
19022
19033
  return lanes.some(
19023
- (l) => !endpointLanes.has(l.id) && (crossesVerticalLine(pill, l.x) || crossesVerticalLine(pill, l.x + l.width))
19034
+ (l) => !endpointLanes.has(l.id) && rectCrossesLaneBorder(pill, l, laneRangeById.get(l.id)) !== void 0
19024
19035
  );
19025
19036
  };
19026
19037
  const assignDetourSides = () => {
@@ -19240,7 +19251,7 @@ function layoutEdges(diag, nodes, lanes = []) {
19240
19251
  );
19241
19252
  const crossesLaneBorder = lanes.some((l) => {
19242
19253
  if (endpointLanes.has(l.id)) return false;
19243
- return crossesVerticalLine(alt, l.x) || crossesVerticalLine(alt, l.x + l.width);
19254
+ return rectCrossesLaneBorder(alt, l, laneRangeById.get(l.id)) !== void 0;
19244
19255
  });
19245
19256
  if (crossesLaneBorder) continue;
19246
19257
  r.baseLabelX = r.labelAltX;
@@ -19531,9 +19542,6 @@ function segmentsBBox2(segs) {
19531
19542
  }
19532
19543
  return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
19533
19544
  }
19534
- function crossesVerticalLine(rect, lineX) {
19535
- return rect.x < lineX - LANE_BORDER_CLEARANCE_TOL && rect.x + rect.w > lineX + LANE_BORDER_CLEARANCE_TOL;
19536
- }
19537
19545
  function distributeBySides(list) {
19538
19546
  const from = list[0].from;
19539
19547
  const tol = 20;