@cardenelabs/cdl 0.48.0 → 0.50.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 +40 -1
- package/dist/index.cjs +195 -99
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +196 -100
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +184 -95
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +185 -96
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/src/kinds/actor.tsx +2 -1
- package/src/kinds/event.tsx +3 -2
- package/src/kinds/function.tsx +2 -1
- package/src/kinds/generic.tsx +5 -4
- package/src/kinds/sequence-board.tsx +2 -1
- package/src/kinds/storage.tsx +4 -3
- package/src/layout/collisions.ts +293 -139
- package/src/layout-with-validation.ts +4 -2
- package/src/render/edges.tsx +2 -1
- package/src/render/mono-text.ts +44 -0
- package/src/visual-validate.ts +62 -6
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 等幅の書体で描く文字の、 `[` と `]` の間にだけ隙間を足す (#817)。
|
|
3
|
+
*
|
|
4
|
+
* `JetBrains Mono` の `[` と `]` は横棒が字の枠いっぱいまで伸びている。 図を画面の幅に合わせて
|
|
5
|
+
* 縮めると (画面上 14px 前後)、 隣り合った 2 本の横棒がつながって `[]` が四角 1 つに見える
|
|
6
|
+
* (`string[]` が `string□` と読める)。 合字ではない = 合字を止めても見え方は変わらない。
|
|
7
|
+
*
|
|
8
|
+
* 隙間は `<tspan dx>` で足す。 文字は増やさないので `textContent` は元のまま (`string[]`) で、
|
|
9
|
+
* 文字を読む側 (検索 / 読み上げ / 写し取り) は変わらない。 `dx` は文字の塊を新しく始めないので、
|
|
10
|
+
* 右寄せ (`text-anchor: end`) も隙間を含めた全体に効く。
|
|
11
|
+
*
|
|
12
|
+
* `][` は横棒が外を向いていてつながらないので、 `[]` の組ごとに足せば足りる。
|
|
13
|
+
*
|
|
14
|
+
* **engine が等幅の書体で描く文字は全てここを通す**。 通していない箇所はソースの走査で落とす
|
|
15
|
+
* (`mono-bracket-gap.test.tsx`)。
|
|
16
|
+
*/
|
|
17
|
+
import { createElement, type ReactNode } from "react";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 隙間の幅 (字の大きさに対する比)。
|
|
21
|
+
*
|
|
22
|
+
* 等倍で撮り比べて、 全ての字の間を 0.06 空けると 2 本の横棒が分かれて読めた。 図はそれより小さく
|
|
23
|
+
* 縮めて描かれることもあるので、 少し余裕を持たせる。 1 組あたり字幅の 1 割に満たず、 字の送り幅の
|
|
24
|
+
* 計算 (等幅を前提に文字数から桁の位置を決める) には入れない。
|
|
25
|
+
*/
|
|
26
|
+
export const 角括弧の隙間 = 0.08;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 文字列の `[]` を `[` と、 前に隙間を持つ `]` に分けて返す。 `[]` を含まない文字と、 文字列でない
|
|
30
|
+
* 値 (数値 / 要素) はそのまま返す。
|
|
31
|
+
*
|
|
32
|
+
* @param text 描く文字
|
|
33
|
+
* @param fontSize その文字の大きさ (隙間はこれに比例する)
|
|
34
|
+
*/
|
|
35
|
+
export function 等幅の字(text: ReactNode, fontSize: number): ReactNode {
|
|
36
|
+
if (typeof text !== "string" || !text.includes("[]")) return text;
|
|
37
|
+
const 塊 = text.split("[]");
|
|
38
|
+
const out: ReactNode[] = [];
|
|
39
|
+
塊.forEach((s, i) => {
|
|
40
|
+
if (i > 0) out.push("[", createElement("tspan", { key: i, dx: fontSize * 角括弧の隙間 }, "]"));
|
|
41
|
+
if (s !== "") out.push(s);
|
|
42
|
+
});
|
|
43
|
+
return out;
|
|
44
|
+
}
|
package/src/visual-validate.ts
CHANGED
|
@@ -88,6 +88,7 @@ import {
|
|
|
88
88
|
flattenPathSegments,
|
|
89
89
|
measurePathLabelGapSegs,
|
|
90
90
|
segmentsCrossRect,
|
|
91
|
+
札が動けない理由,
|
|
91
92
|
} from "./layout/collisions";
|
|
92
93
|
import { measureTextWidth } from "./layout/text-width";
|
|
93
94
|
import { droppedLineIsHidden, labelPlan } from "./kinds/shape-region";
|
|
@@ -276,6 +277,14 @@ export interface Violation {
|
|
|
276
277
|
diagramId: string;
|
|
277
278
|
detail: string;
|
|
278
279
|
severity: "error" | "warn";
|
|
280
|
+
/**
|
|
281
|
+
* engine がなぜ直せなかったか (#810)。 分かる時だけ付く。
|
|
282
|
+
*
|
|
283
|
+
* `detail` とは別の欄に置く = `detail` は「どの 2 つが何 px か」 を固定の形で持ち、
|
|
284
|
+
* 消費側がそこから id を読み取っている。 理由を `detail` に足すと、 理由の中の id まで
|
|
285
|
+
* 違反の対象として拾われる。
|
|
286
|
+
*/
|
|
287
|
+
reason?: string;
|
|
279
288
|
}
|
|
280
289
|
|
|
281
290
|
export type VisualAxis =
|
|
@@ -1447,12 +1456,23 @@ export function visualValidateLaid(
|
|
|
1447
1456
|
const counts = emptyCounts();
|
|
1448
1457
|
const profile = opts?.profile ?? "production";
|
|
1449
1458
|
const skipped = SKIPPED_AXES[profile];
|
|
1450
|
-
const push = (
|
|
1459
|
+
const push = (
|
|
1460
|
+
axis: VisualAxis,
|
|
1461
|
+
detail: string,
|
|
1462
|
+
severity: "error" | "warn" = "error",
|
|
1463
|
+
reason?: string,
|
|
1464
|
+
) => {
|
|
1451
1465
|
// 用途に合わない軸は数えない。
|
|
1452
1466
|
if (skipped.has(axis)) return;
|
|
1453
1467
|
// `diag` は壊れた形でも渡りうる (`#393`)。 報告そのものが例外になると、 壊れている
|
|
1454
1468
|
// ことを伝える手段が無くなる。
|
|
1455
|
-
violations.push({
|
|
1469
|
+
violations.push({
|
|
1470
|
+
axis,
|
|
1471
|
+
diagramId: safeDiagramId(diag),
|
|
1472
|
+
detail,
|
|
1473
|
+
severity,
|
|
1474
|
+
...(reason !== undefined ? { reason } : {}),
|
|
1475
|
+
});
|
|
1456
1476
|
counts[axis]++;
|
|
1457
1477
|
};
|
|
1458
1478
|
|
|
@@ -1479,10 +1499,21 @@ export function visualValidate(diag: CdlDiagram, opts?: ValidateOptions): Visual
|
|
|
1479
1499
|
const counts = emptyCounts();
|
|
1480
1500
|
const profile = opts?.profile ?? "production";
|
|
1481
1501
|
const skipped = SKIPPED_AXES[profile];
|
|
1482
|
-
const push = (
|
|
1502
|
+
const push = (
|
|
1503
|
+
axis: VisualAxis,
|
|
1504
|
+
detail: string,
|
|
1505
|
+
severity: "error" | "warn" = "error",
|
|
1506
|
+
reason?: string,
|
|
1507
|
+
) => {
|
|
1483
1508
|
// 用途に合わない軸は数えない。 見なかった軸は report の `skippedAxes` に載せる。
|
|
1484
1509
|
if (skipped.has(axis)) return;
|
|
1485
|
-
violations.push({
|
|
1510
|
+
violations.push({
|
|
1511
|
+
axis,
|
|
1512
|
+
diagramId: safeDiagramId(diag),
|
|
1513
|
+
detail,
|
|
1514
|
+
severity,
|
|
1515
|
+
...(reason !== undefined ? { reason } : {}),
|
|
1516
|
+
});
|
|
1486
1517
|
counts[axis]++;
|
|
1487
1518
|
};
|
|
1488
1519
|
|
|
@@ -1852,7 +1883,7 @@ function visualValidateInner(
|
|
|
1852
1883
|
diag: CdlDiagram,
|
|
1853
1884
|
violations: Violation[],
|
|
1854
1885
|
counts: Record<VisualAxis, number>,
|
|
1855
|
-
push: (axis: VisualAxis, detail: string, severity?: "error" | "warn") => void,
|
|
1886
|
+
push: (axis: VisualAxis, detail: string, severity?: "error" | "warn", reason?: string) => void,
|
|
1856
1887
|
profile: ValidationProfile,
|
|
1857
1888
|
skippedAxes: VisualAxis[],
|
|
1858
1889
|
/** 軸 23 が基準にする器。 入口で確かめ済 (`器を確かめる`)。 高さ `undefined` は上限なし。 */
|
|
@@ -1909,7 +1940,7 @@ function runAxes(
|
|
|
1909
1940
|
diag: CdlDiagram,
|
|
1910
1941
|
violations: Violation[],
|
|
1911
1942
|
counts: Record<VisualAxis, number>,
|
|
1912
|
-
push: (axis: VisualAxis, detail: string, severity?: "error" | "warn") => void,
|
|
1943
|
+
push: (axis: VisualAxis, detail: string, severity?: "error" | "warn", reason?: string) => void,
|
|
1913
1944
|
profile: ValidationProfile,
|
|
1914
1945
|
skippedAxes: VisualAxis[],
|
|
1915
1946
|
/** 軸 23 が基準にする器。 入口で確かめ済 (`器を確かめる`)。 高さ `undefined` は上限なし。 */
|
|
@@ -2046,10 +2077,35 @@ function runAxes(
|
|
|
2046
2077
|
// Axis 6: clearance (near collision 0 件、 CLEARANCE_POLICY 違反)
|
|
2047
2078
|
// ───────────────────────────────────────────────────────────
|
|
2048
2079
|
const nears = detectNearCollisions(bboxes, laid.edges);
|
|
2080
|
+
/*
|
|
2081
|
+
* 札が絡む違反には、 engine がなぜ動かせなかったかを添える (#810)。
|
|
2082
|
+
*
|
|
2083
|
+
* 動かす側は逃がし pass と同じ = 札どうしなら後に書いた方 (`resolveLabelClearanceAfterPathPasses`
|
|
2084
|
+
* が後の方を動かす)、 札と別の物なら札。 札の絡まない違反 (箱どうし等) には添えない。
|
|
2085
|
+
*/
|
|
2086
|
+
const 札の障害物 = bboxes.filter((b) => b.kind === "node" || b.kind === "lane-label");
|
|
2087
|
+
const 辺の順 = new Map(laid.edges.map((e, i) => [e.id, i] as const));
|
|
2049
2088
|
for (const n of nears) {
|
|
2089
|
+
const aは札 = n.a.kind === "edge-label";
|
|
2090
|
+
const bは札 = n.b.kind === "edge-label";
|
|
2091
|
+
const 動かす =
|
|
2092
|
+
aは札 && bは札
|
|
2093
|
+
? (辺の順.get(n.a.id) ?? 0) > (辺の順.get(n.b.id) ?? 0)
|
|
2094
|
+
? n.a
|
|
2095
|
+
: n.b
|
|
2096
|
+
: aは札
|
|
2097
|
+
? n.a
|
|
2098
|
+
: bは札
|
|
2099
|
+
? n.b
|
|
2100
|
+
: null;
|
|
2101
|
+
const 相手 = 動かす === n.a ? n.b : n.a;
|
|
2102
|
+
const reason =
|
|
2103
|
+
動かす !== null ? (札が動けない理由(動かす.id, 相手, laid.edges, 札の障害物) ?? undefined) : undefined;
|
|
2050
2104
|
push(
|
|
2051
2105
|
"clearance",
|
|
2052
2106
|
`${n.a.kind}:${n.a.id} ↔ ${n.b.kind}:${n.b.id} gap=${n.gap.toFixed(1)}px (need ${n.required}px)`,
|
|
2107
|
+
"error",
|
|
2108
|
+
reason,
|
|
2053
2109
|
);
|
|
2054
2110
|
}
|
|
2055
2111
|
|