@cardenelabs/cdl 0.41.1 → 0.43.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/CHANGELOG.md CHANGED
@@ -5,6 +5,96 @@ CDL (Chainome Diagram Language) の主要変更履歴。
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.43.0] - 2026-09-10
9
+
10
+ ### Fixed
11
+
12
+ - **帯の間隔を箱の縁から数えるようにした** (#789)
13
+
14
+ 帯 (lane) の間隔は、辺の名札を入れるのに要る隙間から決まる。 その隙間の式は
15
+ `名札の幅 + 箱と名札の余白 32 × 2 + 経路の突き出し 40 × 2` で、内訳はどれも
16
+ **箱の縁** が起点になる。 それを 帯の縁 から数えていたため、
17
+ `(帯の幅 − 箱の幅) / 2` を 2 枚分 余計に空けていた。
18
+
19
+ ただし 2 対以上を跨ぐ辺は別で、名札が途中の帯の内側にも入りうる。 その帯は
20
+ 軸 64 `lane-border-clearance` の対象なので、跨ぐ辺だけは 帯の縁 のまま残す。
21
+ 隣り合う 2 帯を結ぶ辺の名札は発着の 2 帯に閉じるので、軸 64 は元から見ていない。
22
+
23
+ 下流の見本帳 444 枚での実測。
24
+
25
+ | 何を | 前 | 後 |
26
+ |---|---|---|
27
+ | 幅が変わった図 | — | 78 枚 (広がった図は 0 枚) |
28
+ | その 78 枚の幅の合計 | 126,204 | 118,721 |
29
+ | 軸 23 `responsive-viewport` | 16 件 | 12 件 |
30
+ | 軸 64 `lane-border-clearance` | 0 件 | 0 件 |
31
+ | 軸 65 `row-gap-uniform` | 0 件 | 0 件 |
32
+
33
+ 最も縮んだのは `swim-demo` の 2148 → 1848 で、親の幅 1100px での箱の題が
34
+ 11.3px から 13.1px になる。
35
+
36
+ **余りに頼っていた図が 2 枚出る**。 名札を 2 枚以上 同じ隙間に置く図は、
37
+ 1 枚分しか確保しない式では元から足りておらず、余りがそれを隠していた
38
+ (実測 = `interactive-traffic-sankey` で 3 件、`animation-rich-order-status-flow` で 1 件)。
39
+ 帯の位置を書き手が広げれば消える。
40
+
41
+ ## [0.42.0] - 2026-09-10
42
+
43
+ ### Fixed
44
+
45
+ - **横長かどうかでなく図の幅で読みやすさを判定するようにした** (軸 23 `responsive-viewport`、#785)
46
+
47
+ 画面上の文字の大きさは `文字の world 寸法 × (親の幅 px ÷ viewBox width)` で決まり、
48
+ この式に縦横比は出てこない。 それでも軸 23 は縦横比 6:1 を境にしていた。
49
+
50
+ 下流の見本帳 444 枚を親の幅 1100px として実測した順位。
51
+
52
+ | 図 | 縦横比 | viewBox 幅 | 画面上の字 | 旧判定で拾えるか |
53
+ |---|---|---|---|---|
54
+ | `class-complex-demo` | 1.4 | 3353 | 7.2px | 拾えない |
55
+ | `interactive-year-roadmap` | 4.0 | 2534 | 9.6px | 拾えない |
56
+ | `lane-multi` | 8.0 | 1535 | 15.8px | 拾える (読める字) |
57
+
58
+ 画面上の字が小さい上位 20 枚のうち旧判定が拾うのは 5 枚だけで、最も小さい図は 1 度も
59
+ 拾われなかった。
60
+
61
+ 判定を viewBox 幅の上限に置き換えた。 上限は 3 つの値から導く = 基準にする文字の world
62
+ 寸法 (箱の題の 22)、判定に使う親の幅 (1100px)、画面上で読めると見なす下限 (12px)。
63
+ 上限そのものは数で書かない。
64
+
65
+ 注意の文面が変わる = 縦横比の代わりに `viewBox width` と、その幅での画面上の文字の
66
+ 大きさを出す。 描画不能な寸法 (0 / 負値 / 非有限) の判定は変えていない。
67
+
68
+ 縦長の図は warn を出さなくなる。 親の幅いっぱいに伸ばすと文字は拡大されるため。
69
+ 読みにくさは縦の巻き取りの量で、文字の大きさではない。
70
+
71
+ ### Removed
72
+
73
+ - **にじみを画面でなく図の座標で測っていた検査 (軸 33 `subpixel-precision`) を外した** (#783)
74
+
75
+ 軸 33 は箱の中心 / 辺の名札 / 帯の world 座標が 0.5 刻みから外れると注意を出していた。
76
+ にじみは device pixel で起きるので、world 座標の小数は判定材料にならない。
77
+
78
+ 図の `<svg>` は `w-full` + `preserveAspectRatio="xMidYMid meet"` で親の幅いっぱいに伸びるため、
79
+ 画面上の位置は `device_x = offsetX + (p − x0) × W/w` で決まり、倍率は器の幅次第の任意の実数になる。
80
+
81
+ 下流の見本帳 `pattern-rollback` (viewBox 幅 2101) で実測した pixel の境目からの端数。
82
+
83
+ | 器の幅 | 倍率 | world 356 (軸の理想値) | world 356.75 (軸が弾いた値) |
84
+ |---|---|---|---|
85
+ | 1100px | 0.523560 | 0.215 | 0.178 |
86
+ | 1200px | 0.571157 | 0.493 | 0.079 |
87
+ | 1400px | 0.666349 | 0.091 | 0.408 |
88
+
89
+ 器 1200px では軸の理想値のほうが境目から遠い。 順位が主張と逆を向いていた。
90
+ 倍率を決めるのは呼び出し側なので、engine 側に代わりに測るべき実体が無い。
91
+
92
+ `VisualAxis` から名前が消え、`report.counts` の同名の鍵も消える。
93
+
94
+ 名札の枠幅を偶数へ切り上げる丸め (`computeLabelBoxW`) は残す。 外すと全ての名札が動く一方、
95
+ 動かして良くなるものが無いため。 座標が 0.5 刻みに乗る性質は
96
+ `test/label-coord-half-grid.test.ts` が引き続き固定する。
97
+
8
98
  ## [0.41.1] - 2026-09-10
9
99
 
10
100
  ### Changed
@@ -2139,7 +2229,9 @@ Initial OSS release.
2139
2229
  使わない = annotated tag を push しても GitHub Release は作られず、 Release を作っていない版で
2140
2230
  行き止まりになる。
2141
2231
 
2142
- [Unreleased]: https://github.com/cardene777/cdl/compare/v0.41.1...HEAD
2232
+ [Unreleased]: https://github.com/cardene777/cdl/compare/v0.43.0...HEAD
2233
+ [0.43.0]: https://github.com/cardene777/cdl/compare/v0.42.0...v0.43.0
2234
+ [0.42.0]: https://github.com/cardene777/cdl/compare/v0.41.1...v0.42.0
2143
2235
  [0.41.1]: https://github.com/cardene777/cdl/compare/v0.41.0...v0.41.1
2144
2236
  [0.41.0]: https://github.com/cardene777/cdl/compare/v0.40.12...v0.41.0
2145
2237
  [0.40.12]: https://github.com/cardene777/cdl/compare/v0.40.11...v0.40.12
package/dist/index.cjs CHANGED
@@ -15535,7 +15535,6 @@ function emptyCounts() {
15535
15535
  "print-media-compat": 0,
15536
15536
  "color-blind-safety": 0,
15537
15537
  "marker-gradient-def-integrity": 0,
15538
- "subpixel-precision": 0,
15539
15538
  "dom-complexity-budget": 0,
15540
15539
  "reduced-motion-compat": 0,
15541
15540
  "touch-target-size": 0,
@@ -16930,7 +16929,10 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
16930
16929
  } catch {
16931
16930
  }
16932
16931
  }
16933
- const RESPONSIVE_MAX_ASPECT = 6;
16932
+ const RESPONSIVE_REF_CONTAINER_PX = 1100;
16933
+ const RESPONSIVE_MIN_ON_SCREEN_FONT_PX = 12;
16934
+ const RESPONSIVE_BASE_FONT_WORLD = 22;
16935
+ const RESPONSIVE_MAX_VIEWBOX_W = RESPONSIVE_BASE_FONT_WORLD * RESPONSIVE_REF_CONTAINER_PX / RESPONSIVE_MIN_ON_SCREEN_FONT_PX;
16934
16936
  const vbResp = laid.viewBox;
16935
16937
  if (!(vbResp.w > 0) || !(vbResp.h > 0) || !Number.isFinite(vbResp.w) || !Number.isFinite(vbResp.h)) {
16936
16938
  push(
@@ -16938,15 +16940,13 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
16938
16940
  `viewBox \u5BF8\u6CD5 ${vbResp.w}\xD7${vbResp.h} \u304C\u63CF\u753B\u4E0D\u80FD (\u6B63\u306E\u6709\u9650\u5024\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308B)`,
16939
16941
  "warn"
16940
16942
  );
16941
- } else {
16942
- const worstAspect = Math.max(vbResp.w / vbResp.h, vbResp.h / vbResp.w);
16943
- if (worstAspect > RESPONSIVE_MAX_ASPECT) {
16944
- push(
16945
- "responsive-viewport",
16946
- `viewBox aspect ${worstAspect.toFixed(1)}:1 \u304C RESPONSIVE_MAX_ASPECT ${RESPONSIVE_MAX_ASPECT}:1 \u8D85\u3048`,
16947
- "warn"
16948
- );
16949
- }
16943
+ } else if (vbResp.w > RESPONSIVE_MAX_VIEWBOX_W) {
16944
+ const \u753B\u9762\u4E0A\u306E\u5B57 = RESPONSIVE_BASE_FONT_WORLD * RESPONSIVE_REF_CONTAINER_PX / vbResp.w;
16945
+ push(
16946
+ "responsive-viewport",
16947
+ `viewBox width ${vbResp.w} \u304C\u4E0A\u9650 ${RESPONSIVE_MAX_VIEWBOX_W.toFixed(0)} \u8D85\u3048 (\u89AA\u5E45 ${RESPONSIVE_REF_CONTAINER_PX}px \u3067\u7BB1\u306E\u984C\u304C ${\u753B\u9762\u4E0A\u306E\u5B57.toFixed(1)}px\u3001 \u4E0B\u9650 ${RESPONSIVE_MIN_ON_SCREEN_FONT_PX}px)`,
16948
+ "warn"
16949
+ );
16950
16950
  }
16951
16951
  if (!(diag.topic?.trim() ?? "")) {
16952
16952
  push(
@@ -17118,39 +17118,6 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
17118
17118
  );
17119
17119
  }
17120
17120
  }
17121
- const SUBPIXEL_TOL = 0.5;
17122
- function isSubpixel(v) {
17123
- const nearestHalf = Math.round(v * 2) / 2;
17124
- return Math.abs(v - nearestHalf) > SUBPIXEL_TOL / 10;
17125
- }
17126
- for (const n of laid.nodes) {
17127
- if (n.id.endsWith("-spacer") || /^s\d+-/.test(n.id)) continue;
17128
- if (isSubpixel(n.cx) || isSubpixel(n.cy)) {
17129
- push(
17130
- "subpixel-precision",
17131
- `node "${n.id}" \u5EA7\u6A19 (${n.cx.toFixed(2)}, ${n.cy.toFixed(2)}) \u304C\u975E\u6574\u6570\u3001 subpixel blur \u30EA\u30B9\u30AF`,
17132
- "warn"
17133
- );
17134
- }
17135
- }
17136
- for (const e of laid.edges) {
17137
- if (isSubpixel(e.labelX) || isSubpixel(e.labelY)) {
17138
- push(
17139
- "subpixel-precision",
17140
- `edge "${e.id}" label \u5EA7\u6A19 (${e.labelX.toFixed(2)}, ${e.labelY.toFixed(2)}) \u304C\u975E\u6574\u6570\u3001 subpixel blur \u30EA\u30B9\u30AF`,
17141
- "warn"
17142
- );
17143
- }
17144
- }
17145
- for (const l of laid.lanes) {
17146
- if (isSubpixel(l.x) || isSubpixel(l.y)) {
17147
- push(
17148
- "subpixel-precision",
17149
- `lane "${l.id}" \u5EA7\u6A19 (${l.x.toFixed(2)}, ${l.y.toFixed(2)}) \u304C\u975E\u6574\u6570\u3001 subpixel blur \u30EA\u30B9\u30AF`,
17150
- "warn"
17151
- );
17152
- }
17153
- }
17154
17121
  const DOM_COMPLEXITY_BUDGET = 400;
17155
17122
  const totalElements = laid.nodes.length + laid.edges.length + laid.lanes.length + diag.phases.length;
17156
17123
  if (totalElements > DOM_COMPLEXITY_BUDGET) {
@@ -17781,7 +17748,6 @@ function validateMetaAxes(totalCounts, diagrams = [], durations = [], reports =
17781
17748
  "print-media-compat",
17782
17749
  "color-blind-safety",
17783
17750
  "marker-gradient-def-integrity",
17784
- "subpixel-precision",
17785
17751
  "dom-complexity-budget",
17786
17752
  "reduced-motion-compat",
17787
17753
  "touch-target-size",
@@ -17850,7 +17816,6 @@ function validateMetaAxes(totalCounts, diagrams = [], durations = [], reports =
17850
17816
  "print-media-compat",
17851
17817
  "color-blind-safety",
17852
17818
  "marker-gradient-def-integrity",
17853
- "subpixel-precision",
17854
17819
  "dom-complexity-budget",
17855
17820
  "reduced-motion-compat",
17856
17821
  "touch-target-size",
@@ -17898,7 +17863,7 @@ function validateMetaAxes(totalCounts, diagrams = [], durations = [], reports =
17898
17863
  });
17899
17864
  }
17900
17865
  }
17901
- const EXPECTED_AXIS_COUNT = 67;
17866
+ const EXPECTED_AXIS_COUNT = 66;
17902
17867
  if (allAxes.length !== EXPECTED_AXIS_COUNT) {
17903
17868
  out.push({
17904
17869
  axis: "axis-documentation-completeness",
@@ -20252,7 +20217,8 @@ function expandLaneGapsForEdgeLabels(lanes, nodes, edges) {
20252
20217
  const nodeById = new Map(nodes.map((n) => [n.id, n]));
20253
20218
  const sortedByX = [...layoutedLanes].sort((a, b) => a.x - b.x);
20254
20219
  const laneIndexById = new Map(sortedByX.map((lane, i) => [lane.id, i]));
20255
- const requiredBoxWByPair = new Array(Math.max(0, sortedByX.length - 1)).fill(0);
20220
+ const requiredBoxWAdjacent = new Array(Math.max(0, sortedByX.length - 1)).fill(0);
20221
+ const requiredBoxWSpanning = new Array(Math.max(0, sortedByX.length - 1)).fill(0);
20256
20222
  for (const edge of edges) {
20257
20223
  const fromLane = nodeById.get(edge.from)?.lane;
20258
20224
  const toLane = nodeById.get(edge.to)?.lane;
@@ -20264,23 +20230,33 @@ function expandLaneGapsForEdgeLabels(lanes, nodes, edges) {
20264
20230
  if (labelBoxW === 0) continue;
20265
20231
  const lo = Math.min(fi, ti);
20266
20232
  const hi = Math.max(fi, ti);
20233
+ const \u8981\u6C42 = hi - lo === 1 ? requiredBoxWAdjacent : requiredBoxWSpanning;
20267
20234
  for (let pair = lo; pair < hi; pair++) {
20268
- if (labelBoxW > requiredBoxWByPair[pair]) requiredBoxWByPair[pair] = labelBoxW;
20235
+ if (labelBoxW > \u8981\u6C42[pair]) \u8981\u6C42[pair] = labelBoxW;
20269
20236
  }
20270
20237
  }
20238
+ const \u7BB1\u306E\u534A\u5E45 = /* @__PURE__ */ new Map();
20239
+ for (const n of nodes) {
20240
+ const \u4ECA = \u7BB1\u306E\u534A\u5E45.get(n.lane) ?? 0;
20241
+ if (n.w / 2 > \u4ECA) \u7BB1\u306E\u534A\u5E45.set(n.lane, n.w / 2);
20242
+ }
20271
20243
  const requiredPitchByPair = [];
20272
20244
  for (let i = 0; i < sortedByX.length - 1; i++) {
20273
20245
  const lane = sortedByX[i];
20274
20246
  const rightLane = sortedByX[i + 1];
20275
- const halfSum = lane.width / 2 + rightLane.width / 2;
20247
+ const laneHalfSum = lane.width / 2 + rightLane.width / 2;
20248
+ const nodeHalfSum = (\u7BB1\u306E\u534A\u5E45.get(lane.id) ?? 0) + (\u7BB1\u306E\u534A\u5E45.get(rightLane.id) ?? 0);
20276
20249
  const currentPitch = rightLane.x + rightLane.width / 2 - (lane.x + lane.width / 2);
20277
- const maxLabelBoxW = requiredBoxWByPair[i];
20278
- if (maxLabelBoxW === 0) {
20250
+ const \u96A3 = requiredBoxWAdjacent[i];
20251
+ const \u8DE8 = requiredBoxWSpanning[i];
20252
+ if (\u96A3 === 0 && \u8DE8 === 0) {
20279
20253
  requiredPitchByPair.push(currentPitch);
20280
20254
  continue;
20281
20255
  }
20282
- const requiredGap = maxLabelBoxW + CLEARANCE_NODE_LABEL * 2 + EDGE_STUB_OUT * 2;
20283
- requiredPitchByPair.push(Math.max(currentPitch, requiredGap + halfSum));
20256
+ const \u4E0A\u4E57\u305B = CLEARANCE_NODE_LABEL * 2 + EDGE_STUB_OUT * 2;
20257
+ const \u96A3\u306E\u8981\u6C42 = \u96A3 === 0 ? 0 : \u96A3 + \u4E0A\u4E57\u305B + nodeHalfSum;
20258
+ const \u8DE8\u306E\u8981\u6C42 = \u8DE8 === 0 ? 0 : \u8DE8 + \u4E0A\u4E57\u305B + laneHalfSum;
20259
+ requiredPitchByPair.push(Math.max(currentPitch, \u96A3\u306E\u8981\u6C42, \u8DE8\u306E\u8981\u6C42));
20284
20260
  }
20285
20261
  const targetPitch = Math.ceil(Math.max(...requiredPitchByPair));
20286
20262
  const shiftedXById = /* @__PURE__ */ new Map();