@cardenelabs/cdl 0.43.0 → 0.45.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,73 @@ CDL (Chainome Diagram Language) の主要変更履歴。
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.45.0] - 2026-09-11
9
+
10
+ ### Added
11
+
12
+ - **読みにくさを測る器を呼び出し側から渡せるようにした** (軸 23 `responsive-viewport`、#797)
13
+
14
+ `visualValidate` / `visualValidateLaid` / `visualValidateAll` に `container` を足した。
15
+
16
+ ```ts
17
+ visualValidate(diagram, { container: { width: 874 } });
18
+ ```
19
+
20
+ 軸 23 は `#793` から器に収めた倍率で読みにくさを判定するが、その器を engine が
21
+ 1150 × 630px に決め打っていた。 これは下流の見本帳の **拡大表示** を画面で実測した値で、
22
+ 同じ見本帳でも一覧に並べた時の枠は幅 874px、縦は巻き取るので上限が無い。
23
+ 器が違えば同じ図でも読めるかどうかが変わるため、決め打つと片方の見せ方が測れないまま残る。
24
+
25
+ **高さは省ける**。 省いた器では縦を見ない = 0 や無限大で表すと「上限が無い」 と
26
+ 「潰れている」 が同じ値になる。 省いた時の文面は `器 幅 874px (高さの上限なし)` になり、
27
+ 効いた辺は必ず横、届く寸法も幅で出す。
28
+
29
+ **壊れた寸法は既定に落とさず例外にする**。 落とすと、誤った値を渡した呼び出し側が
30
+ 「効いている」 と読む。 0 / 負値 / `NaN` / 無限大が対象で、判定は **図ごとではなく入口で
31
+ 1 度だけ**行う (図ごとに投げると、器の誤りが図の破綻に紛れてどちらを直すか読めない)。
32
+ 図を 1 枚も渡さない掃き出しでも確かめる。
33
+
34
+ `container` を渡さない呼び出しは今までどおり拡大表示の器 (1150 × 630px) で判定する。
35
+ 文面も 1 文字も変えていない。
36
+
37
+ ## [0.44.0] - 2026-09-10
38
+
39
+ ### Fixed
40
+
41
+ - **読みにくさを図の幅でなく描かれる倍率で判定するようにした** (軸 23 `responsive-viewport`、#793)
42
+
43
+ 図は `preserveAspectRatio="xMidYMid meet"` で器に収まるよう中で縮む。 効く倍率は
44
+ `min(器の幅 ÷ viewBox 幅, 器の高さ ÷ viewBox 高さ)` で、横が余っていても縦が足りなければ
45
+ 縦が決める。 `#785` で縦横比から幅の上限へ移した時に、**高さの項が抜けていた**。
46
+
47
+ 下流の見本帳を Chromium で開いて実測した (画面 1440 × 900、拡大表示)。
48
+ 28 回の実測のうち 27 回で縦が倍率を決めていた。
49
+
50
+ | 図 | viewBox | 幅から出す見込み | 実測 |
51
+ |---|---|---|---|
52
+ | `多対多が-2-組-8-表-8-関係` | 1255x1700 | 19.3px | 8.2px |
53
+ | `interactive-price-candlestick` | 875x1256 | 27.7px | 11.0px |
54
+ | `fsm-demo` | 1413x1393 | 17.1px | 9.9px |
55
+ | `er-complex-demo` | 2190x2904 | 11.1px | 4.8px |
56
+ | `swim-demo` | 1848x285 | 13.1px | 13.7px |
57
+
58
+ 順位も逆を向いていた。 `er-complex-demo` は見込みで 14 枚中 9 位だが実測は最下位で、
59
+ 拡大すると並べて見る時 (8.8px) より小さくなる。
60
+
61
+ 判定を倍率の下限に置き換えた。 器は画面での実測から導く = 幅は拡大表示の枠
62
+ `min(1200px, 94vw)` から縁 1px × 2 と余白 24px × 2 を引いた 1150px、高さは
63
+ `90vh − 180px` を画面の高さ 900px で評価した 630px。 数は直接書かない。
64
+
65
+ この式に実測の器を入れると、上の 5 枚とも画面での実測と 0.1px まで一致する。
66
+
67
+ 注意の文面が変わる = viewBox の縦横の寸法、器の寸法、倍率、**どちらの辺が効いたか**、
68
+ その辺で下限に届く寸法を出す。 効いた辺を出さないと、幅を削っても直らない図で
69
+ 書き手が幅を削り続けることになる。
70
+
71
+ 下流では注意の件数が増える (実測 = 見本帳 444 枚で 12 件 → 20 件)。
72
+ 増える 10 枚はいずれも縦が長い図で、画面では 8.2-11.9px しかない。
73
+ 描画不能な寸法 (0 / 負値 / 非有限) の判定は変えていない。
74
+
8
75
  ## [0.43.0] - 2026-09-10
9
76
 
10
77
  ### Fixed
@@ -2229,7 +2296,9 @@ Initial OSS release.
2229
2296
  使わない = annotated tag を push しても GitHub Release は作られず、 Release を作っていない版で
2230
2297
  行き止まりになる。
2231
2298
 
2232
- [Unreleased]: https://github.com/cardene777/cdl/compare/v0.43.0...HEAD
2299
+ [Unreleased]: https://github.com/cardene777/cdl/compare/v0.45.0...HEAD
2300
+ [0.45.0]: https://github.com/cardene777/cdl/compare/v0.44.0...v0.45.0
2301
+ [0.44.0]: https://github.com/cardene777/cdl/compare/v0.43.0...v0.44.0
2233
2302
  [0.43.0]: https://github.com/cardene777/cdl/compare/v0.42.0...v0.43.0
2234
2303
  [0.42.0]: https://github.com/cardene777/cdl/compare/v0.41.1...v0.42.0
2235
2304
  [0.41.1]: https://github.com/cardene777/cdl/compare/v0.41.0...v0.41.1
package/dist/index.cjs CHANGED
@@ -15500,6 +15500,27 @@ var SKIPPED_AXES = {
15500
15500
  production: /* @__PURE__ */ new Set(),
15501
15501
  catalog: /* @__PURE__ */ new Set(["seo-metadata-quality", "structured-data-extraction"])
15502
15502
  };
15503
+ var \u65E2\u5B9A\u306E\u5668 = {
15504
+ /**
15505
+ * 拡大表示の枠は `min(1200px, 94vw)` で、中身は 1px の縁と 24px の余白の内側に描かれる。
15506
+ * 画面が 1280px 以上なら 1200 が効くので、そこから縁 2 枚と余白 2 枚を引く。
15507
+ */
15508
+ width: 1200 - 1 * 2 - 24 * 2,
15509
+ /** 同じ拡大表示の高さの頭打ち (`90vh − 180px`) を、基準の画面の高さ 900px で評価した値。 */
15510
+ height: 900 * 0.9 - 180
15511
+ };
15512
+ function \u5668\u3092\u78BA\u304B\u3081\u308B(c) {
15513
+ if (c === void 0) return { \u5E45: \u65E2\u5B9A\u306E\u5668.width, \u9AD8\u3055: \u65E2\u5B9A\u306E\u5668.height };
15514
+ if (!(c.width > 0) || !Number.isFinite(c.width)) {
15515
+ throw new Error(`container.width \u304C\u63CF\u753B\u4E0D\u80FD (\u6B63\u306E\u6709\u9650\u5024\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308B): ${c.width}`);
15516
+ }
15517
+ if (c.height !== void 0 && (!(c.height > 0) || !Number.isFinite(c.height))) {
15518
+ throw new Error(
15519
+ `container.height \u304C\u63CF\u753B\u4E0D\u80FD (\u6B63\u306E\u6709\u9650\u5024\u3067\u6E21\u3059\u304B\u3001\u4E0A\u9650\u304C\u7121\u3044\u306A\u3089\u7701\u304F): ${c.height}`
15520
+ );
15521
+ }
15522
+ return { \u5E45: c.width, \u9AD8\u3055: c.height };
15523
+ }
15503
15524
  var MIN_NODE_W = 80;
15504
15525
  var MIN_NODE_H = 40;
15505
15526
  var ROW_FORMAT = /^[^:]+:\s*.+$/;
@@ -15971,7 +15992,16 @@ function visualValidateLaid(laid, diag, opts) {
15971
15992
  violations.push({ axis, diagramId: safeDiagramId(diag), detail, severity });
15972
15993
  counts[axis]++;
15973
15994
  };
15974
- return visualValidateInner(laid, diag, violations, counts, push, profile, [...skipped]);
15995
+ return visualValidateInner(
15996
+ laid,
15997
+ diag,
15998
+ violations,
15999
+ counts,
16000
+ push,
16001
+ profile,
16002
+ [...skipped],
16003
+ \u5668\u3092\u78BA\u304B\u3081\u308B(opts?.container)
16004
+ );
15975
16005
  }
15976
16006
  function visualValidate(diag, opts) {
15977
16007
  const violations = [];
@@ -16008,7 +16038,16 @@ function visualValidate(diag, opts) {
16008
16038
  } finally {
16009
16039
  console.warn = origWarn;
16010
16040
  }
16011
- return visualValidateInner(laid, safeDiag, violations, counts, push, profile, [...skipped]);
16041
+ return visualValidateInner(
16042
+ laid,
16043
+ safeDiag,
16044
+ violations,
16045
+ counts,
16046
+ push,
16047
+ profile,
16048
+ [...skipped],
16049
+ \u5668\u3092\u78BA\u304B\u3081\u308B(opts?.container)
16050
+ );
16012
16051
  }
16013
16052
  var VALIDATED_ARRAY_FIELDS = [
16014
16053
  ["laid", "lanes"],
@@ -16208,7 +16247,7 @@ function normalizeForValidation(value, problems, owner) {
16208
16247
  }
16209
16248
  return next;
16210
16249
  }
16211
- function visualValidateInner(laid, diag, violations, counts, push, profile, skippedAxes) {
16250
+ function visualValidateInner(laid, diag, violations, counts, push, profile, skippedAxes, \u5668) {
16212
16251
  const problems = findMalformedInput(laid, diag);
16213
16252
  for (const p of problems) push("malformed-input", p.detail, "error");
16214
16253
  if (problems.length > 0) {
@@ -16216,7 +16255,7 @@ function visualValidateInner(laid, diag, violations, counts, push, profile, skip
16216
16255
  diag = normalizeForValidation(diag, problems, "diag");
16217
16256
  }
16218
16257
  try {
16219
- return runAxes(laid, diag, violations, counts, push, profile, skippedAxes);
16258
+ return runAxes(laid, diag, violations, counts, push, profile, skippedAxes, \u5668);
16220
16259
  } catch (err) {
16221
16260
  const known = counts["malformed-input"] > 0;
16222
16261
  const axis = known ? "malformed-input" : "validation-interrupted";
@@ -16229,7 +16268,7 @@ function visualValidateInner(laid, diag, violations, counts, push, profile, skip
16229
16268
  return { diagramId: safeDiagramId(diag), ok: false, violations, counts, profile, skippedAxes };
16230
16269
  }
16231
16270
  }
16232
- function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
16271
+ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes, \u5668) {
16233
16272
  const LIFELINE_W_THRESHOLD = 8;
16234
16273
  for (const n of laid.nodes) {
16235
16274
  if (n.w < LIFELINE_W_THRESHOLD) continue;
@@ -16929,10 +16968,10 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
16929
16968
  } catch {
16930
16969
  }
16931
16970
  }
16932
- const RESPONSIVE_REF_CONTAINER_PX = 1100;
16971
+ const RESPONSIVE_REF_CONTAINER_W_PX = \u5668.\u5E45;
16972
+ const RESPONSIVE_REF_CONTAINER_H_PX = \u5668.\u9AD8\u3055;
16933
16973
  const RESPONSIVE_MIN_ON_SCREEN_FONT_PX = 12;
16934
16974
  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;
16936
16975
  const vbResp = laid.viewBox;
16937
16976
  if (!(vbResp.w > 0) || !(vbResp.h > 0) || !Number.isFinite(vbResp.w) || !Number.isFinite(vbResp.h)) {
16938
16977
  push(
@@ -16940,13 +16979,21 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
16940
16979
  `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)`,
16941
16980
  "warn"
16942
16981
  );
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
- );
16982
+ } else {
16983
+ const \u6A2A\u306E\u500D\u7387 = RESPONSIVE_REF_CONTAINER_W_PX / vbResp.w;
16984
+ const \u7E26\u306E\u500D\u7387 = RESPONSIVE_REF_CONTAINER_H_PX === void 0 ? Number.POSITIVE_INFINITY : RESPONSIVE_REF_CONTAINER_H_PX / vbResp.h;
16985
+ const \u500D\u7387 = Math.min(\u6A2A\u306E\u500D\u7387, \u7E26\u306E\u500D\u7387);
16986
+ const \u753B\u9762\u4E0A\u306E\u5B57 = RESPONSIVE_BASE_FONT_WORLD * \u500D\u7387;
16987
+ if (\u753B\u9762\u4E0A\u306E\u5B57 < RESPONSIVE_MIN_ON_SCREEN_FONT_PX) {
16988
+ const \u52B9\u304F\u8FBA = \u6A2A\u306E\u500D\u7387 <= \u7E26\u306E\u500D\u7387 ? "\u6A2A" : "\u7E26";
16989
+ const \u53CE\u307E\u308B\u5BF8\u6CD5 = \u52B9\u304F\u8FBA === "\u6A2A" || RESPONSIVE_REF_CONTAINER_H_PX === void 0 ? `\u5E45 ${(RESPONSIVE_REF_CONTAINER_W_PX * RESPONSIVE_BASE_FONT_WORLD / RESPONSIVE_MIN_ON_SCREEN_FONT_PX).toFixed(0)} \u4EE5\u4E0B` : `\u9AD8\u3055 ${(RESPONSIVE_REF_CONTAINER_H_PX * RESPONSIVE_BASE_FONT_WORLD / RESPONSIVE_MIN_ON_SCREEN_FONT_PX).toFixed(0)} \u4EE5\u4E0B`;
16990
+ const \u5668\u306E\u66F8\u304D\u65B9 = RESPONSIVE_REF_CONTAINER_H_PX === void 0 ? `\u5668 \u5E45 ${RESPONSIVE_REF_CONTAINER_W_PX}px (\u9AD8\u3055\u306E\u4E0A\u9650\u306A\u3057)` : `\u5668 ${RESPONSIVE_REF_CONTAINER_W_PX}\xD7${RESPONSIVE_REF_CONTAINER_H_PX}px`;
16991
+ push(
16992
+ "responsive-viewport",
16993
+ `viewBox ${vbResp.w}\xD7${vbResp.h} \u306F${\u5668\u306E\u66F8\u304D\u65B9} \u3067 ${\u500D\u7387.toFixed(3)} \u500D\u306B\u7E2E\u307F\u3001 \u7BB1\u306E\u984C\u304C ${\u753B\u9762\u4E0A\u306E\u5B57.toFixed(1)}px \u306B\u306A\u308B (\u4E0B\u9650 ${RESPONSIVE_MIN_ON_SCREEN_FONT_PX}px\u3001 \u500D\u7387\u3092\u6C7A\u3081\u305F\u306E\u306F ${\u52B9\u304F\u8FBA}\u3001 ${\u53CE\u307E\u308B\u5BF8\u6CD5}\u306B\u3059\u308B\u3068\u5C4A\u304F)`,
16994
+ "warn"
16995
+ );
16996
+ }
16950
16997
  }
16951
16998
  if (!(diag.topic?.trim() ?? "")) {
16952
16999
  push(
@@ -17648,6 +17695,7 @@ function runAxes(laid, diag, violations, counts, push, profile, skippedAxes) {
17648
17695
  };
17649
17696
  }
17650
17697
  function visualValidateAll(diagrams, opts) {
17698
+ \u5668\u3092\u78BA\u304B\u3081\u308B(opts?.container);
17651
17699
  const durations = [];
17652
17700
  const reports = diagrams.map((d) => {
17653
17701
  const start = Date.now();