@cardenelabs/cdl 0.78.0 → 0.80.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 +54 -1
- package/dist/index.cjs +139 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +139 -39
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +139 -39
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +139 -39
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/src/layout/collisions.ts +185 -41
- package/src/render/nodes.tsx +16 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,57 @@ CDL (Chainome Diagram Language) の主要変更履歴。
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.80.0] - 2026-09-27
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **押せる箱を指すと押せる形のカーソルが出るようになった** (#869 / #896)
|
|
13
|
+
|
|
14
|
+
`relations: hover` を書いた図の箱は押せる形になるが、指しても `pointer` の cursor が
|
|
15
|
+
出なかった。 読み上げには `role="button"` で「押せる」 と伝わる一方、見た目には出ない =
|
|
16
|
+
読み手は触れて光るだけの箱だと読む。
|
|
17
|
+
|
|
18
|
+
原因は同じ要素に `style` を 2 回渡していたこと。 React は後の値で前を丸ごと置き換える
|
|
19
|
+
ため、押せる箱に渡した `cursor: pointer` が後ろの `transition` に消されていた。
|
|
20
|
+
|
|
21
|
+
| 項目 | 直す前 | 直した後 |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| 押せる箱の `style` | `transition` だけ | `transition` と `cursor: pointer` |
|
|
24
|
+
| 押せない箱の `style` | `transition` だけ | 変えない |
|
|
25
|
+
|
|
26
|
+
`style` を 1 箇所でまとめて作り、要素へ 1 回だけ渡す形にした。 条件つきで広げる側には
|
|
27
|
+
置かない = そこに残すと、後ろに `style` を 1 つ足しただけでまた黙って消える。
|
|
28
|
+
|
|
29
|
+
押せない箱の cursor は親から継承したままにする (編集画面では掴む形になる)。
|
|
30
|
+
|
|
31
|
+
## [0.79.0] - 2026-09-27
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- **円弧を含む線の外接矩形が図の左上まで伸びるのを直した** (#872 / #895)
|
|
36
|
+
|
|
37
|
+
道筋を読む所が **命令ごとの引数の数を見ていなかった**。 文字を全部消して数を 2 つずつ
|
|
38
|
+
組にしていたため、円弧 (`A`) の引数 7 個 = 奇数で組にする位置が 1 つずれ、その後ろの点が
|
|
39
|
+
全部ずれる。 道筋のどこにも無い `(11,11)` と `(0,0)` が点として入っていた。
|
|
40
|
+
|
|
41
|
+
| 何を | 値 |
|
|
42
|
+
|---|---|
|
|
43
|
+
| 道筋の実際の範囲 | x 105..507 / y 278..628 |
|
|
44
|
+
| 直す前の読み方が出す範囲 | x 0..691 / y 0..472 |
|
|
45
|
+
|
|
46
|
+
画面で 358px 離れている名札と線が「重なっている」 と知らされる。 逆向きにも効いており、
|
|
47
|
+
矩形が実際より大きいぶん近接の押し出しが誤った方向に働いていた。
|
|
48
|
+
|
|
49
|
+
**壊れていたのは 3 箇所**。 同じ読み方が散っており、道筋を組み直す所は円弧の `A` を命令と
|
|
50
|
+
して認識できず、前の命令の引数に混ざって `NaN` を書き出していた (円弧を持つ経路を
|
|
51
|
+
近接の解消で動かすと道筋そのものが壊れる)。
|
|
52
|
+
|
|
53
|
+
命令ごとの引数の数を 1 箇所に持ち、3 つの関数がそれを使う形にした。
|
|
54
|
+
|
|
55
|
+
外接矩形は円弧の膨らみを足す。 円弧は終点しか座標を持たないが実際の線は弦から膨らむので
|
|
56
|
+
(経路が経路を跨ぐ飛び越しは半径 11 の半円)、終点だけで矩形を作ると本当の重なりを
|
|
57
|
+
見落とす側に倒れる。
|
|
58
|
+
|
|
8
59
|
## [0.78.0] - 2026-09-26
|
|
9
60
|
|
|
10
61
|
### Fixed
|
|
@@ -3228,7 +3279,9 @@ Initial OSS release.
|
|
|
3228
3279
|
使わない = annotated tag を push しても GitHub Release は作られず、 Release を作っていない版で
|
|
3229
3280
|
行き止まりになる。
|
|
3230
3281
|
|
|
3231
|
-
[Unreleased]: https://github.com/cardene777/cdl/compare/v0.
|
|
3282
|
+
[Unreleased]: https://github.com/cardene777/cdl/compare/v0.80.0...HEAD
|
|
3283
|
+
[0.80.0]: https://github.com/cardene777/cdl/compare/v0.79.0...v0.80.0
|
|
3284
|
+
[0.79.0]: https://github.com/cardene777/cdl/compare/v0.78.0...v0.79.0
|
|
3232
3285
|
[0.78.0]: https://github.com/cardene777/cdl/compare/v0.77.0...v0.78.0
|
|
3233
3286
|
[0.77.0]: https://github.com/cardene777/cdl/compare/v0.76.0...v0.77.0
|
|
3234
3287
|
[0.76.0]: https://github.com/cardene777/cdl/compare/v0.75.0...v0.76.0
|
package/dist/index.cjs
CHANGED
|
@@ -12798,72 +12798,169 @@ function collectBBoxes(lanes, nodes, edges) {
|
|
|
12798
12798
|
});
|
|
12799
12799
|
}
|
|
12800
12800
|
for (const e of edges) {
|
|
12801
|
-
const
|
|
12802
|
-
if (
|
|
12803
|
-
const xs = points.map((p) => p.x);
|
|
12804
|
-
const ys = points.map((p) => p.y);
|
|
12805
|
-
const minX = Math.min(...xs);
|
|
12806
|
-
const maxX = Math.max(...xs);
|
|
12807
|
-
const minY = Math.min(...ys);
|
|
12808
|
-
const maxY = Math.max(...ys);
|
|
12801
|
+
const box = pathBBox(e.d);
|
|
12802
|
+
if (!box) continue;
|
|
12809
12803
|
out.push({
|
|
12810
12804
|
kind: "edge-path",
|
|
12811
12805
|
id: e.id,
|
|
12812
|
-
x: minX - 2,
|
|
12813
|
-
y: minY - 2,
|
|
12814
|
-
w: maxX - minX + 4,
|
|
12815
|
-
h: maxY - minY + 4
|
|
12806
|
+
x: box.minX - 2,
|
|
12807
|
+
y: box.minY - 2,
|
|
12808
|
+
w: box.maxX - box.minX + 4,
|
|
12809
|
+
h: box.maxY - box.minY + 4
|
|
12816
12810
|
});
|
|
12817
12811
|
}
|
|
12818
12812
|
return out;
|
|
12819
12813
|
}
|
|
12814
|
+
var PATH_ARITY = {
|
|
12815
|
+
M: 2,
|
|
12816
|
+
L: 2,
|
|
12817
|
+
H: 1,
|
|
12818
|
+
V: 1,
|
|
12819
|
+
C: 6,
|
|
12820
|
+
S: 4,
|
|
12821
|
+
Q: 4,
|
|
12822
|
+
T: 2,
|
|
12823
|
+
A: 7,
|
|
12824
|
+
Z: 0
|
|
12825
|
+
};
|
|
12826
|
+
function isRelative(cmd) {
|
|
12827
|
+
return cmd >= "a" && cmd <= "z";
|
|
12828
|
+
}
|
|
12829
|
+
function coordSlots(cmd) {
|
|
12830
|
+
switch (cmd.toUpperCase()) {
|
|
12831
|
+
case "M":
|
|
12832
|
+
case "L":
|
|
12833
|
+
case "T":
|
|
12834
|
+
return [[0, 1]];
|
|
12835
|
+
case "H":
|
|
12836
|
+
return [[0, -1]];
|
|
12837
|
+
case "V":
|
|
12838
|
+
return [[-1, 0]];
|
|
12839
|
+
case "Q":
|
|
12840
|
+
case "S":
|
|
12841
|
+
return [[0, 1], [2, 3]];
|
|
12842
|
+
case "C":
|
|
12843
|
+
return [[0, 1], [2, 3], [4, 5]];
|
|
12844
|
+
case "A":
|
|
12845
|
+
return [[5, 6]];
|
|
12846
|
+
default:
|
|
12847
|
+
return [];
|
|
12848
|
+
}
|
|
12849
|
+
}
|
|
12850
|
+
function parsePathCommands(d) {
|
|
12851
|
+
const out = [];
|
|
12852
|
+
const commandRegex = /([MmLlHhVvCcSsQqTtAaZz])([^MmLlHhVvCcSsQqTtAaZz]*)/g;
|
|
12853
|
+
let m;
|
|
12854
|
+
while ((m = commandRegex.exec(d)) !== null) {
|
|
12855
|
+
const cmd = m[1];
|
|
12856
|
+
const arity = PATH_ARITY[cmd.toUpperCase()] ?? 0;
|
|
12857
|
+
if (arity === 0) {
|
|
12858
|
+
out.push({ cmd, args: [] });
|
|
12859
|
+
continue;
|
|
12860
|
+
}
|
|
12861
|
+
const nums = (m[2] ?? "").trim().split(/[\s,]+/).filter(Boolean).map(parseFloat);
|
|
12862
|
+
for (let i = 0; i + arity <= nums.length; i += arity) {
|
|
12863
|
+
const args = nums.slice(i, i + arity);
|
|
12864
|
+
if (!args.every(Number.isFinite)) break;
|
|
12865
|
+
const \u7E70\u308A\u8FD4\u3057 = i > 0;
|
|
12866
|
+
const c = \u7E70\u308A\u8FD4\u3057 && cmd === "M" ? "L" : \u7E70\u308A\u8FD4\u3057 && cmd === "m" ? "l" : cmd;
|
|
12867
|
+
out.push({ cmd: c, args });
|
|
12868
|
+
}
|
|
12869
|
+
}
|
|
12870
|
+
return out;
|
|
12871
|
+
}
|
|
12820
12872
|
function extractPathPoints(d) {
|
|
12821
12873
|
const out = [];
|
|
12822
|
-
|
|
12823
|
-
|
|
12824
|
-
|
|
12825
|
-
const
|
|
12826
|
-
|
|
12874
|
+
let cur = { x: 0, y: 0 };
|
|
12875
|
+
let started = false;
|
|
12876
|
+
for (const { cmd, args } of parsePathCommands(d)) {
|
|
12877
|
+
const \u76F8\u5BFE = started && isRelative(cmd);
|
|
12878
|
+
const \u57FA\u70B9 = cur;
|
|
12879
|
+
for (const [xi, yi] of coordSlots(cmd)) {
|
|
12880
|
+
const x = xi >= 0 ? \u76F8\u5BFE ? \u57FA\u70B9.x + args[xi] : args[xi] : \u57FA\u70B9.x;
|
|
12881
|
+
const y = yi >= 0 ? \u76F8\u5BFE ? \u57FA\u70B9.y + args[yi] : args[yi] : \u57FA\u70B9.y;
|
|
12827
12882
|
out.push({ x, y });
|
|
12883
|
+
cur = { x, y };
|
|
12828
12884
|
}
|
|
12885
|
+
started = true;
|
|
12829
12886
|
}
|
|
12830
12887
|
return out;
|
|
12831
12888
|
}
|
|
12889
|
+
function pathBBox(d) {
|
|
12890
|
+
let minX = Infinity;
|
|
12891
|
+
let minY = Infinity;
|
|
12892
|
+
let maxX = -Infinity;
|
|
12893
|
+
let maxY = -Infinity;
|
|
12894
|
+
let \u70B9\u3042\u308A = false;
|
|
12895
|
+
const \u53D6\u308B = (x, y) => {
|
|
12896
|
+
minX = Math.min(minX, x);
|
|
12897
|
+
minY = Math.min(minY, y);
|
|
12898
|
+
maxX = Math.max(maxX, x);
|
|
12899
|
+
maxY = Math.max(maxY, y);
|
|
12900
|
+
\u70B9\u3042\u308A = true;
|
|
12901
|
+
};
|
|
12902
|
+
let cur = { x: 0, y: 0 };
|
|
12903
|
+
let started = false;
|
|
12904
|
+
for (const { cmd, args } of parsePathCommands(d)) {
|
|
12905
|
+
const \u76F8\u5BFE = started && isRelative(cmd);
|
|
12906
|
+
const \u57FA\u70B9 = cur;
|
|
12907
|
+
for (const [xi, yi] of coordSlots(cmd)) {
|
|
12908
|
+
const x = xi >= 0 ? \u76F8\u5BFE ? \u57FA\u70B9.x + args[xi] : args[xi] : \u57FA\u70B9.x;
|
|
12909
|
+
const y = yi >= 0 ? \u76F8\u5BFE ? \u57FA\u70B9.y + args[yi] : args[yi] : \u57FA\u70B9.y;
|
|
12910
|
+
\u53D6\u308B(x, y);
|
|
12911
|
+
cur = { x, y };
|
|
12912
|
+
}
|
|
12913
|
+
if (cmd.toUpperCase() === "A") {
|
|
12914
|
+
const r = Math.max(Math.abs(args[0]), Math.abs(args[1]));
|
|
12915
|
+
const \u5F26 = Math.hypot(cur.x - \u57FA\u70B9.x, cur.y - \u57FA\u70B9.y);
|
|
12916
|
+
const \u534A\u5F26 = Math.min(\u5F26 / 2, r);
|
|
12917
|
+
const \u4F59 = Math.sqrt(Math.max(0, r * r - \u534A\u5F26 * \u534A\u5F26));
|
|
12918
|
+
const \u81A8\u3089\u307F = args[3] !== 0 ? r + \u4F59 : r - \u4F59;
|
|
12919
|
+
\u53D6\u308B(Math.min(\u57FA\u70B9.x, cur.x) - \u81A8\u3089\u307F, Math.min(\u57FA\u70B9.y, cur.y) - \u81A8\u3089\u307F);
|
|
12920
|
+
\u53D6\u308B(Math.max(\u57FA\u70B9.x, cur.x) + \u81A8\u3089\u307F, Math.max(\u57FA\u70B9.y, cur.y) + \u81A8\u3089\u307F);
|
|
12921
|
+
}
|
|
12922
|
+
started = true;
|
|
12923
|
+
}
|
|
12924
|
+
return \u70B9\u3042\u308A ? { minX, minY, maxX, maxY } : null;
|
|
12925
|
+
}
|
|
12832
12926
|
function rebuildPathD(d, map) {
|
|
12833
|
-
const commandRegex = /([MLCQZmlcqz])([^MLCQZmlcqz]*)/g;
|
|
12834
12927
|
let out = "";
|
|
12835
12928
|
let idx = 0;
|
|
12836
|
-
let
|
|
12837
|
-
|
|
12838
|
-
|
|
12839
|
-
const
|
|
12840
|
-
if (
|
|
12929
|
+
let cur = { x: 0, y: 0 };
|
|
12930
|
+
let started = false;
|
|
12931
|
+
for (const { cmd, args } of parsePathCommands(d)) {
|
|
12932
|
+
const slots = coordSlots(cmd);
|
|
12933
|
+
if (slots.length === 0) {
|
|
12841
12934
|
out += cmd + " ";
|
|
12935
|
+
started = true;
|
|
12842
12936
|
continue;
|
|
12843
12937
|
}
|
|
12844
|
-
const
|
|
12845
|
-
const
|
|
12846
|
-
|
|
12847
|
-
|
|
12848
|
-
|
|
12938
|
+
const \u76F8\u5BFE = started && isRelative(cmd);
|
|
12939
|
+
const \u57FA\u70B9 = cur;
|
|
12940
|
+
const \u6B21\u306E\u5F15\u6570 = args.slice();
|
|
12941
|
+
for (const [xi, yi] of slots) {
|
|
12942
|
+
const x = xi >= 0 ? \u76F8\u5BFE ? \u57FA\u70B9.x + args[xi] : args[xi] : \u57FA\u70B9.x;
|
|
12943
|
+
const y = yi >= 0 ? \u76F8\u5BFE ? \u57FA\u70B9.y + args[yi] : args[yi] : \u57FA\u70B9.y;
|
|
12944
|
+
const [nx, ny] = map(x, y, idx);
|
|
12945
|
+
if (!\u76F8\u5BFE) {
|
|
12946
|
+
if (xi >= 0) \u6B21\u306E\u5F15\u6570[xi] = nx;
|
|
12947
|
+
if (yi >= 0) \u6B21\u306E\u5F15\u6570[yi] = ny;
|
|
12948
|
+
}
|
|
12849
12949
|
idx += 1;
|
|
12950
|
+
cur = { x, y };
|
|
12850
12951
|
}
|
|
12851
|
-
out += cmd + " " +
|
|
12952
|
+
out += cmd + " " + \u6B21\u306E\u5F15\u6570.join(" ") + " ";
|
|
12953
|
+
started = true;
|
|
12852
12954
|
}
|
|
12853
12955
|
return out.trim();
|
|
12854
12956
|
}
|
|
12855
12957
|
function cornerBoundaryIndices(d) {
|
|
12856
|
-
const commandRegex = /([MLCQZmlcqz])([^MLCQZmlcqz]*)/g;
|
|
12857
12958
|
let idx = 0;
|
|
12858
12959
|
let firstCornerEnd = -1;
|
|
12859
12960
|
let lastCornerStart = -1;
|
|
12860
|
-
|
|
12861
|
-
|
|
12862
|
-
|
|
12863
|
-
const argsStr = (m[2] ?? "").trim();
|
|
12864
|
-
if (argsStr === "" || cmd === "Z" || cmd === "z") continue;
|
|
12865
|
-
const pairs = argsStr.split(/[\s,]+/).filter(Boolean).length >> 1;
|
|
12866
|
-
if ((cmd === "Q" || cmd === "q") && pairs >= 2) {
|
|
12961
|
+
for (const { cmd } of parsePathCommands(d)) {
|
|
12962
|
+
const pairs = coordSlots(cmd).length;
|
|
12963
|
+
if (cmd.toUpperCase() === "Q" && pairs >= 2) {
|
|
12867
12964
|
if (firstCornerEnd < 0) firstCornerEnd = idx + 1;
|
|
12868
12965
|
lastCornerStart = idx;
|
|
12869
12966
|
}
|
|
@@ -32011,6 +32108,10 @@ function CdlNodeView({
|
|
|
32011
32108
|
const offsetX = node.renderOffsetX !== void 0 ? resolveNumericAttr(node.renderOffsetX, stateValues, 0) : 0;
|
|
32012
32109
|
const offsetY = node.renderOffsetY !== void 0 ? resolveNumericAttr(node.renderOffsetY, stateValues, 0) : 0;
|
|
32013
32110
|
const hasOffset = offsetX !== 0 || offsetY !== 0;
|
|
32111
|
+
const \u7BB1\u306Estyle = {
|
|
32112
|
+
transition: "opacity 120ms ease-out, transform 200ms ease-out",
|
|
32113
|
+
...\u95A2\u4FC2\u306E\u53D7\u3051\u53E3 === void 0 ? {} : { cursor: "pointer" }
|
|
32114
|
+
};
|
|
32014
32115
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
32015
32116
|
"g",
|
|
32016
32117
|
{
|
|
@@ -32032,7 +32133,6 @@ function CdlNodeView({
|
|
|
32032
32133
|
role: "button",
|
|
32033
32134
|
tabIndex: 0,
|
|
32034
32135
|
"aria-pressed": \u95A2\u4FC2\u306E\u53D7\u3051\u53E3.\u7126\u70B9 === node.id,
|
|
32035
|
-
style: { cursor: "pointer" },
|
|
32036
32136
|
onPointerEnter: () => \u95A2\u4FC2\u306E\u53D7\u3051\u53E3.\u89E6\u308C\u305F(node.id),
|
|
32037
32137
|
onPointerLeave: () => \u95A2\u4FC2\u306E\u53D7\u3051\u53E3.\u96E2\u308C\u305F(),
|
|
32038
32138
|
onFocus: () => \u95A2\u4FC2\u306E\u53D7\u3051\u53E3.\u89E6\u308C\u305F(node.id),
|
|
@@ -32047,7 +32147,7 @@ function CdlNodeView({
|
|
|
32047
32147
|
transform: hasOffset ? `translate(${offsetX}, ${offsetY})` : void 0,
|
|
32048
32148
|
opacity: boundOpacity,
|
|
32049
32149
|
"data-cdl-tone": node.tone,
|
|
32050
|
-
style:
|
|
32150
|
+
style: \u7BB1\u306Estyle,
|
|
32051
32151
|
children: [
|
|
32052
32152
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
32053
32153
|
"rect",
|