@cardenelabs/cdl 0.59.1 → 0.60.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 +27 -1
- package/dist/index.cjs +34 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -4
- package/dist/index.d.ts +12 -4
- package/dist/index.js +34 -11
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +29 -7
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +29 -7
- package/dist/react.js.map +1 -1
- package/dist/{render-DpwY2hzM.d.cts → render-MbIjRWdX.d.cts} +7 -0
- package/dist/{render-DpwY2hzM.d.ts → render-MbIjRWdX.d.ts} +7 -0
- package/package.json +1 -1
- package/src/builder.ts +13 -5
- package/src/formula/parser.ts +32 -3
- package/src/render/interactive-panel.tsx +18 -5
- package/src/types.ts +7 -0
|
@@ -2463,6 +2463,13 @@ type CdlFormula = {
|
|
|
2463
2463
|
id: string;
|
|
2464
2464
|
/** safe subset expression 文字列。 例 `"gasBase * (1 + delta/8)"` */
|
|
2465
2465
|
expression: string;
|
|
2466
|
+
/**
|
|
2467
|
+
* 操作部の式の行に描く名札 (#853)。 無ければ id を描く。
|
|
2468
|
+
*
|
|
2469
|
+
* 名札があると、式の中身の名前のうち名札を持つ入力欄と式の id も名札に置き換えて描く。
|
|
2470
|
+
* 置き換えは表示だけで、計算は `expression` を読む。
|
|
2471
|
+
*/
|
|
2472
|
+
label?: string;
|
|
2466
2473
|
};
|
|
2467
2474
|
/**
|
|
2468
2475
|
* event handler の bind target (CAR #246)。 node / edge / lane の id 指定 or diagram 全体 (root)。
|
|
@@ -2463,6 +2463,13 @@ type CdlFormula = {
|
|
|
2463
2463
|
id: string;
|
|
2464
2464
|
/** safe subset expression 文字列。 例 `"gasBase * (1 + delta/8)"` */
|
|
2465
2465
|
expression: string;
|
|
2466
|
+
/**
|
|
2467
|
+
* 操作部の式の行に描く名札 (#853)。 無ければ id を描く。
|
|
2468
|
+
*
|
|
2469
|
+
* 名札があると、式の中身の名前のうち名札を持つ入力欄と式の id も名札に置き換えて描く。
|
|
2470
|
+
* 置き換えは表示だけで、計算は `expression` を読む。
|
|
2471
|
+
*/
|
|
2472
|
+
label?: string;
|
|
2466
2473
|
};
|
|
2467
2474
|
/**
|
|
2468
2475
|
* event handler の bind target (CAR #246)。 node / edge / lane の id 指定 or diagram 全体 (root)。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cardenelabs/cdl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.60.0",
|
|
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",
|
package/src/builder.ts
CHANGED
|
@@ -1596,8 +1596,10 @@ export type DiagramBuilder = {
|
|
|
1596
1596
|
* consumer が createFormulaComputeds() で computed 化して signal と reactive bind する。
|
|
1597
1597
|
* expression syntax error は本 method 内で throw (fail-fast)。
|
|
1598
1598
|
* 例 `.formula("gasFee", "gasBase * (1 + delta/8)")`
|
|
1599
|
+
*
|
|
1600
|
+
* `opts.label` は操作部の式の行に描く名札 (#853)。 例 `.formula("total", "baseFee + tip", { label: "合計" })`
|
|
1599
1601
|
*/
|
|
1600
|
-
formula: (id: string, expression: string) => DiagramBuilder;
|
|
1602
|
+
formula: (id: string, expression: string, opts?: { label?: string }) => DiagramBuilder;
|
|
1601
1603
|
/**
|
|
1602
1604
|
* derive chain = 「連鎖形 formula」 を N 個宣言的に生成 (CAR: derive chain)。
|
|
1603
1605
|
* 各 iteration で `{i}` 置換と、 直前 iteration の id (`prev`) を参照可能。
|
|
@@ -1607,11 +1609,14 @@ export type DiagramBuilder = {
|
|
|
1607
1609
|
*
|
|
1608
1610
|
* baseId は「gas1 / gas2 / gas3」 のように `gas${i+1}` として使われる、
|
|
1609
1611
|
* expr fn は index (0..count-1) と prev id (前 iteration の完全 id) を受け取る。
|
|
1612
|
+
*
|
|
1613
|
+
* `opts.label` は index を受け取り、その式の名札を返す (#853)。 例 `{ label: (i) => \`${i + 1} 回目\` }`
|
|
1610
1614
|
*/
|
|
1611
1615
|
deriveChain: (
|
|
1612
1616
|
baseId: string,
|
|
1613
1617
|
count: number,
|
|
1614
1618
|
exprFn: (i: number, prev: string | null) => string,
|
|
1619
|
+
opts?: { label?: (i: number) => string },
|
|
1615
1620
|
) => DiagramBuilder;
|
|
1616
1621
|
/**
|
|
1617
1622
|
* animation primitive (CAR #245)。 `.animation.scroll(id, opts)` で scroll-driven trigger を追加。
|
|
@@ -2213,7 +2218,7 @@ export function diagram(
|
|
|
2213
2218
|
return api;
|
|
2214
2219
|
},
|
|
2215
2220
|
},
|
|
2216
|
-
formula(formulaId, expression) {
|
|
2221
|
+
formula(formulaId, expression, opts) {
|
|
2217
2222
|
assertUniqueFormulaId(formulaId);
|
|
2218
2223
|
// 入口で parse を試み、 syntax error は formula id 付きで throw (fail-fast)
|
|
2219
2224
|
try {
|
|
@@ -2222,10 +2227,11 @@ export function diagram(
|
|
|
2222
2227
|
const msg = e instanceof Error ? e.message : String(e);
|
|
2223
2228
|
throw new Error(`formula '${formulaId}' の expression syntax error: ${msg}`);
|
|
2224
2229
|
}
|
|
2225
|
-
|
|
2230
|
+
// 名札の無い式は `label` を持たせない (空の文字列も持たせない = 名札の無い図の形を変えない)
|
|
2231
|
+
formulas.push({ id: formulaId, expression, ...(opts?.label ? { label: opts.label } : {}) });
|
|
2226
2232
|
return api;
|
|
2227
2233
|
},
|
|
2228
|
-
deriveChain(baseId, count, exprFn) {
|
|
2234
|
+
deriveChain(baseId, count, exprFn, opts) {
|
|
2229
2235
|
if (!Number.isInteger(count) || count < 1 || count > 100) {
|
|
2230
2236
|
throw new Error(`deriveChain: count (${count}) は 1..100 の整数である必要があります`);
|
|
2231
2237
|
}
|
|
@@ -2242,7 +2248,9 @@ export function diagram(
|
|
|
2242
2248
|
const msg = e instanceof Error ? e.message : String(e);
|
|
2243
2249
|
throw new Error(`deriveChain '${id}' の expression syntax error: ${msg}`);
|
|
2244
2250
|
}
|
|
2245
|
-
|
|
2251
|
+
const label = opts?.label?.(i);
|
|
2252
|
+
// 名札の無い式は `label` を持たせない (空の文字列も持たせない = 名札の無い図の形を変えない)
|
|
2253
|
+
formulas.push({ id, expression: expr, ...(label ? { label } : {}) });
|
|
2246
2254
|
prev = id;
|
|
2247
2255
|
}
|
|
2248
2256
|
return api;
|
package/src/formula/parser.ts
CHANGED
|
@@ -21,7 +21,8 @@ import { isValidTemplateName } from "../template-name";
|
|
|
21
21
|
|
|
22
22
|
type Token =
|
|
23
23
|
| { kind: "num"; value: number; pos: number }
|
|
24
|
-
|
|
24
|
+
/** `end` は字句の直後の位置。 `{name}` の形は閉じ括弧の直後 (表示用の置き換えが字句ごと差し替える) */
|
|
25
|
+
| { kind: "id"; name: string; pos: number; end: number }
|
|
25
26
|
| { kind: "math"; fn: MathFnName; pos: number }
|
|
26
27
|
| { kind: "op"; op: BinaryOp; pos: number }
|
|
27
28
|
| { kind: "lparen"; pos: number }
|
|
@@ -92,7 +93,7 @@ function tokenize(src: string): Token[] {
|
|
|
92
93
|
`invalid name "{${name}}" at position ${start} (空白 / { } / . / [ ] は名前に使えない)`,
|
|
93
94
|
);
|
|
94
95
|
}
|
|
95
|
-
tokens.push({ kind: "id", name, pos: start });
|
|
96
|
+
tokens.push({ kind: "id", name, pos: start, end: close + 1 });
|
|
96
97
|
i = close + 1;
|
|
97
98
|
continue;
|
|
98
99
|
}
|
|
@@ -156,7 +157,7 @@ function tokenize(src: string): Token[] {
|
|
|
156
157
|
`property access "${name}.${src[i + 1] ?? ""}..." at position ${start} is not allowed (only Math.* namespace is accessible)`,
|
|
157
158
|
);
|
|
158
159
|
}
|
|
159
|
-
tokens.push({ kind: "id", name, pos: start });
|
|
160
|
+
tokens.push({ kind: "id", name, pos: start, end: i });
|
|
160
161
|
continue;
|
|
161
162
|
}
|
|
162
163
|
// operators
|
|
@@ -401,6 +402,34 @@ export function parseFormula(expression: string): FormulaAst {
|
|
|
401
402
|
return new Parser(tokens).parse();
|
|
402
403
|
}
|
|
403
404
|
|
|
405
|
+
/**
|
|
406
|
+
* 式の中身の名前を、`名札` に載る名前だけ名札に置き換えた表示用の字を返す (#853)。 **計算には使わない**。
|
|
407
|
+
*
|
|
408
|
+
* 置き換えは字句の位置で行う。 文字列の検索で探すと、`gas1` の置き換えが `gas10` の先頭に当たり、
|
|
409
|
+
* 値の `min` の置き換えが `Math.min` の中に当たる。 関数の名前 (`Math.min` / 直後が `(` の `min`) と数は
|
|
410
|
+
* 名前の字句にならないので置き換わらない。 名札が空の文字列の名前は置き換えない。
|
|
411
|
+
*
|
|
412
|
+
* 字句に切れない式 (構文の誤り) は、元の字をそのまま返す。
|
|
413
|
+
*/
|
|
414
|
+
export function 式を名札で読む(expression: string, 名札: ReadonlyMap<string, string>): string {
|
|
415
|
+
let tokens: Token[];
|
|
416
|
+
try {
|
|
417
|
+
tokens = tokenize(expression);
|
|
418
|
+
} catch {
|
|
419
|
+
return expression;
|
|
420
|
+
}
|
|
421
|
+
let 読み = "";
|
|
422
|
+
let 前 = 0;
|
|
423
|
+
for (const t of tokens) {
|
|
424
|
+
if (t.kind !== "id") continue;
|
|
425
|
+
const 札 = 名札.get(t.name);
|
|
426
|
+
if (札 === undefined || 札 === "") continue;
|
|
427
|
+
読み += expression.slice(前, t.pos) + 札;
|
|
428
|
+
前 = t.end;
|
|
429
|
+
}
|
|
430
|
+
return 読み + expression.slice(前);
|
|
431
|
+
}
|
|
432
|
+
|
|
404
433
|
/**
|
|
405
434
|
* AST 内で参照される identifier (signal 名) を全て収集する。 evaluator が signal 経由で
|
|
406
435
|
* runtime value を lookup する時の識別子リスト作成に使う。
|
|
@@ -5,6 +5,7 @@ import { assertNever } from "../assert-never";
|
|
|
5
5
|
import type { CdlDiagram, CdlInput, CdlReadout } from "../types";
|
|
6
6
|
import { createInputSignals, inputDefaultValue } from "../input-signals";
|
|
7
7
|
import { createFormulaComputeds } from "../formula";
|
|
8
|
+
import { 式を名札で読む } from "../formula/parser";
|
|
8
9
|
import {
|
|
9
10
|
createScrollProgressSignals,
|
|
10
11
|
type ScrollProgressHandle,
|
|
@@ -643,6 +644,10 @@ export function useInteractiveIntegration(
|
|
|
643
644
|
readouts.length === 0
|
|
644
645
|
)
|
|
645
646
|
return null;
|
|
647
|
+
// 式の中身の名前を読む名札 (#853)。 名札を持つ入力欄と式だけを載せる。
|
|
648
|
+
// 巻き取りは載せない = 式は入力欄と前の式しか参照できず (`createFormulaComputeds` が他の名前を拒む)、巻き取りの名前は中身に出ない
|
|
649
|
+
const 名札 = new Map<string, string>();
|
|
650
|
+
for (const x of [...inputs, ...formulasList]) if (x.label) 名札.set(x.id, x.label);
|
|
646
651
|
return (
|
|
647
652
|
// 操作部の中の読み出しと入力欄へ、図の言語を届ける (#849)。 型の外の値は英語に落ちる
|
|
648
653
|
<操作部の言語.Provider value={resolveLang(diagram.lang)}>
|
|
@@ -660,7 +665,9 @@ export function useInteractiveIntegration(
|
|
|
660
665
|
<FormulaReadout
|
|
661
666
|
key={f.id}
|
|
662
667
|
id={f.id}
|
|
663
|
-
|
|
668
|
+
name={f.label || f.id}
|
|
669
|
+
// 名札の無い式は中身も書いたまま描く。 入力欄の名札は多くの図にあるので、置き換えると名札を付けていない式の描き方まで変わる
|
|
670
|
+
expression={f.label ? 式を名札で読む(f.expression, 名札) : f.expression}
|
|
664
671
|
value={stateOverrides[f.id]}
|
|
665
672
|
/>
|
|
666
673
|
))}
|
|
@@ -669,7 +676,7 @@ export function useInteractiveIntegration(
|
|
|
669
676
|
{scrolls.length > 0 && (
|
|
670
677
|
<div className="cdl-ip-group">
|
|
671
678
|
{scrolls.map((s) => (
|
|
672
|
-
<ScrollReadout key={s.id} id={s.id} progress={stateOverrides[s.id]} />
|
|
679
|
+
<ScrollReadout key={s.id} id={s.id} name={s.label || s.id} progress={stateOverrides[s.id]} />
|
|
673
680
|
))}
|
|
674
681
|
</div>
|
|
675
682
|
)}
|
|
@@ -966,30 +973,36 @@ function NumberInputWidget({
|
|
|
966
973
|
);
|
|
967
974
|
}
|
|
968
975
|
|
|
976
|
+
/** 式の行。 `name` は名札か id (#853)、目印 `data-cdl-formula` は名札の有無によらず id */
|
|
969
977
|
function FormulaReadout({
|
|
970
978
|
id,
|
|
979
|
+
name,
|
|
971
980
|
expression,
|
|
972
981
|
value,
|
|
973
982
|
}: {
|
|
974
983
|
id: string;
|
|
984
|
+
name: string;
|
|
975
985
|
expression: string;
|
|
976
986
|
value: unknown;
|
|
977
987
|
}): JSX.Element {
|
|
978
988
|
return (
|
|
979
989
|
<div className="cdl-ip-item cdl-ip-formula" data-cdl-formula={id}>
|
|
980
|
-
<span className="cdl-ip-label">{
|
|
990
|
+
<span className="cdl-ip-label">{name}</span>
|
|
981
991
|
<code className="cdl-ip-formula-expr">= {expression}</code>
|
|
982
992
|
<span className="cdl-ip-formula-value">{String(value ?? "")}</span>
|
|
983
993
|
</div>
|
|
984
994
|
);
|
|
985
995
|
}
|
|
986
996
|
|
|
987
|
-
|
|
997
|
+
/**
|
|
998
|
+
* 巻き取りの進みの行。 `name` は名札か id (#853)。 型と組み立ては `label` を持っていたが、この行は id を描いていた
|
|
999
|
+
*/
|
|
1000
|
+
function ScrollReadout({ id, name, progress }: { id: string; name: string; progress: unknown }): JSX.Element {
|
|
988
1001
|
const p = typeof progress === "number" ? progress : 0;
|
|
989
1002
|
const pct = Math.round(p * 100);
|
|
990
1003
|
return (
|
|
991
1004
|
<div className="cdl-ip-item cdl-ip-scroll" data-cdl-scroll={id}>
|
|
992
|
-
<span className="cdl-ip-label">{
|
|
1005
|
+
<span className="cdl-ip-label">{name}</span>
|
|
993
1006
|
<div className="cdl-ip-scroll-track">
|
|
994
1007
|
<div className="cdl-ip-scroll-fill" style={{ width: `${pct}%` }} />
|
|
995
1008
|
</div>
|
package/src/types.ts
CHANGED
|
@@ -2514,6 +2514,13 @@ export type CdlFormula = {
|
|
|
2514
2514
|
id: string;
|
|
2515
2515
|
/** safe subset expression 文字列。 例 `"gasBase * (1 + delta/8)"` */
|
|
2516
2516
|
expression: string;
|
|
2517
|
+
/**
|
|
2518
|
+
* 操作部の式の行に描く名札 (#853)。 無ければ id を描く。
|
|
2519
|
+
*
|
|
2520
|
+
* 名札があると、式の中身の名前のうち名札を持つ入力欄と式の id も名札に置き換えて描く。
|
|
2521
|
+
* 置き換えは表示だけで、計算は `expression` を読む。
|
|
2522
|
+
*/
|
|
2523
|
+
label?: string;
|
|
2517
2524
|
};
|
|
2518
2525
|
|
|
2519
2526
|
/**
|