@cardenelabs/cdl 0.17.0 → 0.17.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cardenelabs/cdl",
3
- "version": "0.17.0",
3
+ "version": "0.17.1",
4
4
  "description": "CDL (Chainome Diagram Language). Mermaid-like declarative DSL that compiles to animated SVG diagrams. Built for blockchain / Solidity flows, generic enough for sequence / flow / state / ER / topology diagrams.",
5
5
  "license": "MIT",
6
6
  "author": "cardene777",
@@ -68,7 +68,15 @@ export function SequenceBoardNode({
68
68
  const v = Number.parseInt(interpolate(node.sequenceStep, stateValues ?? {}).trim(), 10);
69
69
  return Number.isFinite(v) ? v : null;
70
70
  })();
71
- const 朱 = "var(--cdl-tone-accent, #2d6a8f)";
71
+ /*
72
+ * いまの言づての色 (#580)。
73
+ *
74
+ * **意味の 6 色とは別に持つ**。 `--cdl-tone-accent` は「呼び」 のような図の中の意味を
75
+ * 描き分ける色で、利用側が系統色から取ることがある (実測 = dragon は青)。 ここが指すのは
76
+ * 意味ではなく「いまどこか」 で、役割は強調した箱の枠と同じ = 利用側の主役色に揃うべき。
77
+ * 書かれていなければ従来どおり意味の色へ落ちる。
78
+ */
79
+ const 朱 = "var(--cdl-now, var(--cdl-tone-accent, #2d6a8f))";
72
80
  /** 済んだ言づての色。 薄めるのではなく墨へ移すので、後からでも読める濃さを持つ */
73
81
  const 墨 = "var(--cdl-text-2, #4a5162)";
74
82
 
@@ -7,6 +7,7 @@ import {
7
7
  ROW_TOP_STORAGE,
8
8
  STORAGE_ROW_DIVIDER_Y,
9
9
  STORAGE_TITLE_BASELINE,
10
+ STORAGE_TITLE_FONT,
10
11
  SUBTITLE_ROW_SHIFT,
11
12
  rowBaselineY,
12
13
  rowGlyphDepth,
@@ -37,7 +38,7 @@ const ROW_GLYPH_CX = ROW_PAD_X_PX + ROW_GLYPH_HALF + 1;
37
38
  const ROW_GLYPH_LEAD = 40;
38
39
 
39
40
  /** 印を持つ行の型の級。 名前 (26) より 1 段落とし、色も薄くして名前を先に読ませる */
40
- const ROW_TYPE_FONT = 24;
41
+ const ROW_TYPE_FONT = 22;
41
42
 
42
43
  /** 印の中心を baseline からどれだけ上へ置くか。 級の 1/3 が字の見た目の中心 */
43
44
  const ROW_GLYPH_RISE = 8;
@@ -48,10 +49,10 @@ const ROW_GLYPH_RISE = 8;
48
49
  * 題 (78) と 1 行目 (130) の間。 印で分ける箱では区切り線を引かないので、その分の余白が空く。
49
50
  * ER の表名や状態の名前は識別子なので、その下に日本語の呼び名を薄く添える。
50
51
  */
51
- const SUBTITLE_BASELINE = 108;
52
+ const SUBTITLE_BASELINE = 118;
52
53
 
53
54
  /** 呼び名の級。 行の名前 (26) より小さく、型 (24) と同じ */
54
- const SUBTITLE_FONT = 22;
55
+ const SUBTITLE_FONT = 20;
55
56
 
56
57
  /** 主キーの下線を名前の baseline からどれだけ下に引くか */
57
58
  const UNDERLINE_DROP = 7;
@@ -98,7 +99,7 @@ export function StorageNode({
98
99
  const x = node.cx - node.w / 2;
99
100
  const y = node.cy - node.h / 2;
100
101
  // 小型の箱では名前を中央に置く。 固定の位置のままだと下端をまたぐ (#416)
101
- const 名前 = titlePlacement(node, { x: 26, y: STORAGE_TITLE_BASELINE, size: 22 }, { hasEyebrow: Boolean(node.eyebrow) });
102
+ const 名前 = titlePlacement(node, { x: 26, y: STORAGE_TITLE_BASELINE, size: STORAGE_TITLE_FONT }, { hasEyebrow: Boolean(node.eyebrow) });
102
103
  // 全 row を delimiter (`:` / ` = ` / ` → ` / ` -> `) で left / right に分割し、
103
104
  // left 列 max 文字数を算出 → 右列の固定 x 位置を決める (DB table / UML class box 風)。
104
105
  // 結果として全 row の右列開始 x が揃い、 区切り線がガタつかなくなる。
@@ -189,18 +189,19 @@ export function layoutNodes(diag: CdlDiagram, lanes: LaidLane[]): LaidNode[] {
189
189
  const 段の芯 = rowCy.get(n.stack)!;
190
190
  const 段の高さ = rowH.get(n.stack)!;
191
191
  /*
192
- * 行頭の印を持つ箱は **自分の高さで、段の上端に揃える** (#578)。
192
+ * 行頭の印を持つ箱は **自分の高さで、段の芯に揃える** (#578 → #580)。
193
193
  *
194
- * 既定は段の高さに合わせて伸ばし、芯で揃える = 同じ段の箱の上端も下端も揃う。 行が
195
- * 1 つしか無い箱が 4 行の箱と同じ高さになるので、下が空いたまま並ぶ。
194
+ * 既定は段の高さに合わせて伸ばす = 行が 1 つしか無い箱が 4 行の箱と同じ高さになり、
195
+ * 下が空いたまま並ぶ。 印を持つ箱は高さを中身から出しているので (`autoStorageHeight`)、
196
+ * 伸ばすとその値が捨てられる。
196
197
  *
197
- * 印を持つ箱では高さを中身から出しているので (`autoStorageHeight`)、伸ばすとその値が
198
- * 捨てられる。 上端で揃えるのは、読み手が箱の題を横に見比べる順に読むため = 下端は
199
- * 中身の量で決まってよい。
198
+ * **揃える先は芯**。 #578 では上端に揃えていたが、高さの違う箱が横に並ぶと
199
+ * 図全体が上へ寄って見えた (user 指摘)。 芯で揃えると上端も下端も箱ごとに違うが、
200
+ * 並びの重心が 1 本に通る。 見た目検査の `row-alignment` もこの前提で書かれている。
200
201
  */
201
202
  const 自前の高さ = n.rowMarks !== undefined;
202
203
  const h = n.h ?? (自前の高さ ? autoStorageHeight(n, size.h) : 段の高さ);
203
- const cy = 自前の高さ ? 段の芯 - 段の高さ / 2 + h / 2 : 段の芯;
204
+ const cy = 段の芯;
204
205
  // n.w 明示時は尊重、 未指定で rows[] を持つ node は内容長に合わせて auto 拡張。
205
206
  const w = autoRowsWidth(n, size.w);
206
207
  out.push({
@@ -242,8 +242,13 @@ export function genericTitleBaselineY(kind: string | undefined, h: number, hasRo
242
242
  : GENERIC_TITLE_TOP + genericTextShift(kind);
243
243
  }
244
244
 
245
- /** `kinds/storage.tsx` の 1 行目 baseline と行送り。 */
246
- export const ROW_TOP_STORAGE = 130;
245
+ /**
246
+ * `kinds/storage.tsx` 1 行目 baseline と行送り。
247
+ *
248
+ * 1 行目は #1468 で 130 から下げた。 題を 22 から 34 に上げたぶん、題の下に空きが要る
249
+ * (実測 = 130 のままだと題の descender と 1 行目の cap が 6 まで詰まる)。
250
+ */
251
+ export const ROW_TOP_STORAGE = 152;
247
252
  export const ROW_PITCH_STORAGE = 56;
248
253
 
249
254
  /** `kinds/generic.tsx` の 1 行目 baseline と行送り。 */
@@ -276,8 +281,21 @@ export const GLYPH_CAP_RATIO = 0.72;
276
281
  export const ROW_FONT_STORAGE = 26;
277
282
  export const ROW_FONT_GENERIC = 21;
278
283
 
279
- /** `kinds/storage.tsx` の名前 (title) の baseline。 */
280
- export const STORAGE_TITLE_BASELINE = 78;
284
+ /**
285
+ * `kinds/storage.tsx` の名前 (title) の baseline。
286
+ *
287
+ * #1468 で 78 から下げた。 題が 34 になり、上端から baseline までに cap 分の余白が要る。
288
+ */
289
+ export const STORAGE_TITLE_BASELINE = 86;
290
+
291
+ /**
292
+ * `kinds/storage.tsx` の名前 (title) の字の大きさ (#1468)。
293
+ *
294
+ * **行 (`ROW_FONT_STORAGE` = 26) より大きい**。 22 だった間は題の方が小さく、箱の中で
295
+ * 何が見出しか読み取れなかった (user 指摘)。 題 34 / 行 26 / 型 22 の 3 段にして、
296
+ * 上から順に読む順序を字の大きさで示す。
297
+ */
298
+ export const STORAGE_TITLE_FONT = 34;
281
299
 
282
300
  /**
283
301
  * 呼び名を出す箱で、行の群をどれだけ下げるか (#578)。
@@ -125,6 +125,7 @@ export type { EdgeLabelLine, EdgeLabelTextSpec } from "./label-text";
125
125
  import {
126
126
  ARROW_ENDPOINT_CENTER_TOL,
127
127
  COLUMN_GAP_VARIANCE_TOL,
128
+ requiredNearClearance,
128
129
  DETOUR_CREST_OVERLAP_MIN,
129
130
  DETOUR_SLOT_GAP,
130
131
  EDGE_STUB_OUT,
@@ -2493,18 +2494,9 @@ function runAxes(
2493
2494
  for (const [stack, group] of byStack) {
2494
2495
  if (group.length < 2) continue;
2495
2496
  const cy0 = group[0]!.cy;
2496
- /*
2497
- * **上端で揃える形も認める** (#578 の設計)。
2498
- *
2499
- * 高さの違う箱を横に並べる図 (表 / クラス / 状態) では、中心ではなく上端を揃える。
2500
- * 中心だけを見ると、正しく上端で揃っている行が高さの差の半分だけずれて見える
2501
- * (実測 = 行数の違う表で 28 の差)。 どちらかで揃っていれば揃っているとみなす。
2502
- */
2503
- const top0 = cy0 - (group[0]!.h ?? 0) / 2;
2504
2497
  for (const n of group.slice(1)) {
2505
2498
  const diff = Math.abs(n.cy - cy0);
2506
- const topDiff = Math.abs(n.cy - (n.h ?? 0) / 2 - top0);
2507
- if (diff > MIN_ROW_ALIGNMENT_TOLERANCE && topDiff > MIN_ROW_ALIGNMENT_TOLERANCE) {
2499
+ if (diff > MIN_ROW_ALIGNMENT_TOLERANCE) {
2508
2500
  push(
2509
2501
  "row-alignment",
2510
2502
  `stack=${stack} row 内 node "${n.id}" cy=${n.cy.toFixed(1)} が row 基準 cy=${cy0.toFixed(1)} と ${diff.toFixed(1)} world 差 (許容 ${MIN_ROW_ALIGNMENT_TOLERANCE})`,
@@ -4239,24 +4231,37 @@ function runAxes(
4239
4231
  const sorted = [...group].sort((a, b) => a.cy - b.cy);
4240
4232
  const gaps: number[] = [];
4241
4233
  /*
4242
- * **格子に置く形では上端どうしの間隔で測る** (#578 の設計)。
4243
- *
4244
- * 段の位置を著者が決める図 (クラス / 表 / 状態) では、engine が制御しているのは
4245
- * 段の基準線であって端の間隔ではない。 箱の高さが段ごとに違うと端の間隔は必ずばらつく
4246
- * (実測 = クラス図の 2 列目で 132 と 300)。 どちらかが揃っていれば揃っているとみなす。
4234
+ * 端の間隔で測る理由は下の Issue #202 の節を参照。
4247
4235
  */
4248
- const tops: number[] = [];
4249
4236
  for (let i = 1; i < sorted.length; i++) {
4250
4237
  const prev = sorted[i - 1]!;
4251
4238
  const cur = sorted[i]!;
4252
4239
  gaps.push(cur.cy - cur.h / 2 - (prev.cy + prev.h / 2));
4253
- tops.push(cur.cy - cur.h / 2 - (prev.cy - prev.h / 2));
4254
4240
  }
4255
4241
  const maxGap = Math.max(...gaps);
4256
4242
  const minGap = Math.min(...gaps);
4257
4243
  const variance = maxGap - minGap;
4258
- const topVariance = Math.max(...tops) - Math.min(...tops);
4259
- if (variance > COLUMN_GAP_VARIANCE_TOL && topVariance > COLUMN_GAP_VARIANCE_TOL) {
4244
+ /*
4245
+ * **格子に置いた箱では間隔の均一を求めない** (#580)
4246
+ *
4247
+ * この軸は「engine が縦の間隔を制御している」 ことを前提に、そのばらつきを layout の
4248
+ * 破綻として見る。 行頭の印を持つ箱は前提が違う = 段の位置を著者が `col` / `row` で決め、
4249
+ * 高さは中身から出る (`autoStorageHeight`)。 段ごとに背の高さが違えば、端の間隔も芯の
4250
+ * 間隔も必ずばらつく (実測 = クラス図の 2 列目で端 132/216、芯 528/472)。
4251
+ *
4252
+ * **近づき過ぎだけは見る** (#202)。 間隔の均一を外しても、箱どうしが接する形は通さない
4253
+ * (#202 が実測した形 = 芯の間隔が揃い、端の間隔が 100 と 0)。
4254
+ */
4255
+ const 格子に置く = group.every((n) => n.rowMarks !== undefined);
4256
+ const 端が足りている = minGap >= requiredNearClearance("node", "node");
4257
+ if (格子に置く) {
4258
+ if (!端が足りている) {
4259
+ push(
4260
+ "column-gap-uniform",
4261
+ `lane "${laneId}" 内 node 間の端間 gap ${minGap.toFixed(0)} world が下限 ${requiredNearClearance("node", "node")} を割る`,
4262
+ );
4263
+ }
4264
+ } else if (variance > COLUMN_GAP_VARIANCE_TOL) {
4260
4265
  push(
4261
4266
  "column-gap-uniform",
4262
4267
  `lane "${laneId}" 内 node 間の端間 gap variance ${variance.toFixed(1)} world が spec ${COLUMN_GAP_VARIANCE_TOL} 超過 (min=${minGap.toFixed(0)} max=${maxGap.toFixed(0)})`,