@cardenelabs/cdl 0.74.0 → 0.76.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.
@@ -1,5 +1,11 @@
1
1
  import type { JSX } from "react";
2
- import { shapeRegion, labelPlan, fitArt } from "./shape-region";
2
+ import {
3
+ shapeRegion,
4
+ labelPlan,
5
+ fitArt,
6
+ 打ち消しの倍率,
7
+ type 打ち消す字,
8
+ } from "./shape-region";
3
9
  import type { LaidNode } from "../types";
4
10
  import { 字引きを作る, type CdlLang } from "../lang";
5
11
 
@@ -193,7 +199,7 @@ export function ShapeBankNode({ node, active }: ShapeProps): JSX.Element {
193
199
  })}
194
200
  <path d={`M ${x - BANK_BODY_OUT} ${y + pedimentH} L ${r.cx} ${y - BANK_PEDIMENT_RISE} L ${x + bldW + BANK_BODY_OUT} ${y + pedimentH} Z`} fill="var(--cdl-node-fill, #ffffff)" stroke={frame.stroke} strokeWidth={frame.strokeWidth} strokeLinejoin="round" />
195
201
  <circle cx={r.cx} cy={y + pedimentH / 2 + 4} r={14} fill="var(--cdl-tone-accent, #2d6a8f)" stroke={frame.stroke} strokeWidth={1.5} />
196
- <text x={r.cx} y={y + pedimentH / 2 + 10} fontSize={18} fontWeight={700} textAnchor="middle" fill="#ffffff" fontFamily="Georgia, serif">
202
+ <text data-cdl-counter-scaled="0" x={r.cx} y={y + pedimentH / 2 + 10} fontSize={18} fontWeight={700} textAnchor="middle" fill="#ffffff" fontFamily="Georgia, serif">
197
203
  $
198
204
  </text>
199
205
  <rect x={x - 4} y={y + pedimentH} width={bldW + 8} height={architraveH} fill="var(--cdl-divider, #d4d4d8)" fillOpacity={0.4} stroke={frame.stroke} strokeWidth={frame.strokeWidth} />
@@ -278,7 +284,7 @@ export function ShapeTrustBankNode({ node, active, lang }: ShapeProps): JSX.Elem
278
284
  return <rect key={i} x={r.cx - w / 2} y={y + bldH + i * BANK_STEP_DY} width={w} height={BANK_STEP_H} fill="var(--cdl-divider, #d4d4d8)" fillOpacity={0.5 + i * 0.15} stroke={frame.stroke} strokeWidth={BANK_STEP_SW} />;
279
285
  })}
280
286
  <path d={`M ${x - 6} ${y + pedimentH} L ${r.cx} ${y + 2} L ${x + bldW + 6} ${y + pedimentH} Z`} fill="var(--cdl-node-fill, #ffffff)" stroke={frame.stroke} strokeWidth={frame.strokeWidth} strokeLinejoin="round" />
281
- <text x={r.cx} y={y + pedimentH - 8} fontSize={15} fontWeight={700} letterSpacing={2} textAnchor="middle" fill="var(--cdl-tone-accent, #2d6a8f)" fontFamily="Georgia, serif">
287
+ <text data-cdl-counter-scaled="0" x={r.cx} y={y + pedimentH - 8} fontSize={15} fontWeight={700} letterSpacing={2} textAnchor="middle" fill="var(--cdl-tone-accent, #2d6a8f)" fontFamily="Georgia, serif">
282
288
  {字引("trustBank.sign")}
283
289
  </text>
284
290
  <rect x={x - 4} y={y + pedimentH} width={bldW + 8} height={architraveH} fill="var(--cdl-divider, #d4d4d8)" fillOpacity={0.4} stroke={frame.stroke} strokeWidth={frame.strokeWidth} />
@@ -338,7 +344,7 @@ export function ShapePaymentProviderNode({ node, active, lang }: ShapeProps): JS
338
344
  <text x={cx} y={cy + 9} fontSize={13} fontWeight={700} textAnchor="middle" fill={accentDarker} fontFamily="Georgia, serif">$</text>
339
345
  </g>
340
346
  {/* PROCESSING label */}
341
- <text x={cx} y={y + boxH - 16} fontSize={9} fontWeight={700} letterSpacing={2} textAnchor="middle" fill="#a4b5f2">{字引("paymentProvider.status")}</text>
347
+ <text x={cx} y={y + boxH - 16} fontSize={11} fontWeight={700} letterSpacing={2} textAnchor="middle" fill="#a4b5f2">{字引("paymentProvider.status")}</text>
342
348
  {bottomLabel(node, r.cx, r.label)}
343
349
  </g>
344
350
  );
@@ -392,10 +398,20 @@ export function ShapeCreditCardNode({ node, active }: ShapeProps): JSX.Element {
392
398
  *
393
399
  * 影と床帯はビルの外へ出る。 出る量は箱の高さに依らないので、 描ける帯を先に狭める。
394
400
  */
395
- const BROKERAGE_TICK_H = 12;
401
+ const BROKERAGE_TICK_H = 18;
396
402
  const BROKERAGE_LOGO_H = 26;
403
+ /**
404
+ * 電光掲示板の内側の帯と、そこに流れる相場の字の指定。
405
+ *
406
+ * 帯の高さは **字の指定から決める** (#888)。 打ち消しの上限は `帯の高さ x 0.8 / 指定` なので、
407
+ * 帯が `指定 x 0.8` を下回ると上限が 1 を切り、打ち消しが 1 度も効かなくなる。
408
+ */
409
+ const BROKERAGE_TICKER_SIZE = 11;
410
+ const BROKERAGE_TICK_INNER_H = 14;
411
+ /** ロゴ帯の看板の字の指定。 */
412
+ const BROKERAGE_SIGN_SIZE = 12;
397
413
  /** 窓の格子。 上端はロゴ帯の下、 下端は基壇の上。 */
398
- const BROKERAGE_GRID_TOP = 62;
414
+ const BROKERAGE_GRID_TOP = 68;
399
415
  const BROKERAGE_CELL_W = 14;
400
416
  const BROKERAGE_CELL_H = 18;
401
417
  const BROKERAGE_GAP_X = 8;
@@ -454,7 +470,22 @@ export function ShapeBrokerageNode({ node, active, lang }: ShapeProps): JSX.Elem
454
470
  );
455
471
  const gridStartX = x + (w - (cols * cellW + (cols - 1) * gapX)) / 2;
456
472
  const goldCells = new Set([`0,1`, `0,4`, `1,2`, `2,3`, `3,5`]); // ランダム点灯 window
457
- const 絵 = (
473
+ /*
474
+ * 打ち消しの上限を出す帯 (#887)。 相場の字は電光掲示板の内側 (幅 w - 4、高さ 8) に入る。
475
+ *
476
+ * **看板 `$ SECURITIES` は打ち消さない**。 字間 2.5 を持ち幅が字数に比例するため、言語で
477
+ * 字数が変われば帯を超える (#886 の記号と同じ扱い)。
478
+ */
479
+ const 打ち消す字ら: 打ち消す字[] = [
480
+ {
481
+ text: 字引("brokerage.ticker"),
482
+ size: BROKERAGE_TICKER_SIZE,
483
+ 帯: { 幅: w - 4, 高さ: BROKERAGE_TICK_INNER_H },
484
+ },
485
+ ];
486
+ const 絵 = (倍率: number): JSX.Element => {
487
+ const 倍 = 打ち消しの倍率(倍率, 打ち消す字ら);
488
+ return (
458
489
  <g>
459
490
  {/* drop shadow */}
460
491
  <ellipse cx={cx} cy={y + h + BROKERAGE_SHADOW_DY} rx={w * BROKERAGE_SHADOW_RX_RATIO} ry={BROKERAGE_SHADOW_RY} fill={frame.stroke} opacity={0.2} stroke="none" />
@@ -464,13 +495,13 @@ export function ShapeBrokerageNode({ node, active, lang }: ShapeProps): JSX.Elem
464
495
  <rect x={x - BROKERAGE_SHADE_OUT} y={y} width={BROKERAGE_SHADE_OUT * 2} height={h} fill={accentDarker} />
465
496
  {/* 上部電光掲示板 tick */}
466
497
  <rect x={x} y={y} width={w} height={BROKERAGE_TICK_H} fill="#0f1830" stroke="none" />
467
- <rect x={x + 2} y={y + 2} width={w - 4} height={8} fill="#1a2340" stroke="none" />
468
- <text x={cx} y={y + 9} fontSize={5.5} fontWeight={700} textAnchor="middle" fill="#4ea36a" fontFamily="ui-monospace, SFMono-Regular, Menlo, monospace">
498
+ <rect x={x + 2} y={y + 2} width={w - 4} height={BROKERAGE_TICK_INNER_H} fill="#1a2340" stroke="none" />
499
+ <text data-cdl-counter-scaled="1" x={cx} y={y + 12} fontSize={BROKERAGE_TICKER_SIZE * 倍} fontWeight={700} textAnchor="middle" fill="#4ea36a" fontFamily="ui-monospace, SFMono-Regular, Menlo, monospace">
469
500
  {字引("brokerage.ticker")}
470
501
  </text>
471
502
  {/* $ SECURITIES ロゴ帯 */}
472
503
  <rect x={x} y={y + BROKERAGE_TICK_H} width={w} height={BROKERAGE_LOGO_H} fill="#e5c07b" stroke="none" />
473
- <text x={cx} y={y + 30} fontSize={12} fontWeight={700} letterSpacing={2.5} textAnchor="middle" fill="#0f1830">
504
+ <text data-cdl-counter-scaled="0" x={cx} y={y + BROKERAGE_TICK_H + 18} fontSize={BROKERAGE_SIGN_SIZE} fontWeight={700} letterSpacing={2.5} textAnchor="middle" fill="#0f1830">
474
505
  {字引("brokerage.sign")}
475
506
  </text>
476
507
  {/* window grid (段数は高さ次第、 列は 6 固定) */}
@@ -492,7 +523,8 @@ export function ShapeBrokerageNode({ node, active, lang }: ShapeProps): JSX.Elem
492
523
  {/* 底部床 (ATM のように狭め) */}
493
524
  <rect x={x - BROKERAGE_FLOOR_OUT} y={y + h + BROKERAGE_FLOOR_DY} width={w + BROKERAGE_FLOOR_OUT * 2} height={BROKERAGE_FLOOR_H} fill="#5a6270" stroke="none" />
494
525
  </g>
495
- );
526
+ );
527
+ };
496
528
  return (
497
529
  <g>
498
530
  {fitArt(r, 広がり, 絵)}
@@ -502,6 +534,11 @@ export function ShapeBrokerageNode({ node, active, lang }: ShapeProps): JSX.Elem
502
534
  }
503
535
 
504
536
  /** shape-exchange = E2-A 中央 clearing house (hexagon EXCHANGE + 放射 8 node + 双方向 tx dots) */
537
+ /** 六角形の中に置く清算の字の指定。 */
538
+ const EXCHANGE_SUB_SIZE = 11;
539
+ /** 中央の六角形の外接半径。 看板の字 (指定 11、 字間 1 で幅 83.7) が入る大きさから決めた (#888)。 */
540
+ const EXCHANGE_HEX_R = 52;
541
+
505
542
  export function ShapeExchangeNode({ node, active, lang }: ShapeProps): JSX.Element {
506
543
  const 字引 = 字引きを作る(lang);
507
544
  const frame = commonFrame(active);
@@ -523,7 +560,15 @@ export function ShapeExchangeNode({ node, active, lang }: ShapeProps): JSX.Eleme
523
560
  };
524
561
  });
525
562
  // 中央 hexagon = 縦向き正六角形 (上下に頂点、 30° 回転)
526
- const hexR = 45;
563
+ const hexR = EXCHANGE_HEX_R;
564
+ /**
565
+ * 六角形の中で、中心から縦に `d` 離れた高さに引ける横幅。
566
+ *
567
+ * 上下の頂点へ向かって細るので、字の帯を `hexR * 2` で見積もると入らない字を入ると
568
+ * 判定する。 **字の下端の高さで測る** = そこが字の占める範囲でいちばん狭い (#888)。
569
+ */
570
+ const 六角の横幅 = (d: number): number =>
571
+ hexR * Math.sqrt(3) * Math.min(1, Math.max(0, (hexR - Math.abs(d)) / (hexR / 2)));
527
572
  const hexPoints = [-90, -30, 30, 90, 150, 210].map((deg) => {
528
573
  const rad = (deg * Math.PI) / 180;
529
574
  return { x: cx + Math.cos(rad) * hexR, y: cy + Math.sin(rad) * hexR };
@@ -540,7 +585,7 @@ export function ShapeExchangeNode({ node, active, lang }: ShapeProps): JSX.Eleme
540
585
  */
541
586
  const 節点 = Math.max(0, nodeR) + 8 + 1.5 / 2;
542
587
  const 六角 = hexR + frame.strokeWidth / 2;
543
- const 字下 = 26 + 8 * 0.25;
588
+ const 字下 = 28 + EXCHANGE_SUB_SIZE * 0.25;
544
589
  const 外 = Math.max(節点, 六角);
545
590
  const ずれ = cy - r.cy;
546
591
  const 広がり = {
@@ -549,7 +594,24 @@ export function ShapeExchangeNode({ node, active, lang }: ShapeProps): JSX.Eleme
549
594
  左: 外,
550
595
  右: 外,
551
596
  };
552
- const 絵 = (
597
+ /*
598
+ * 打ち消しの上限を出す帯 (#887)。 清算の字は六角形の下寄りに置くので、幅はその高さで
599
+ * 引ける横幅を採る (#888 で `外 * 2` から改めた。 絵の横幅は六角形の外まで含むため、
600
+ * 入らない字を入ると判定していた)。 高さは記号との間隔に合わせて 14 を採る。
601
+ *
602
+ * **看板 `EXCHANGE` と記号 `⇄` は打ち消さない**。 前者は字間 1 を持ち幅が字数に比例し、
603
+ * 後者は六角形の中に余白を持たない。
604
+ */
605
+ const 打ち消す字ら: 打ち消す字[] = [
606
+ {
607
+ text: 字引("exchange.sub"),
608
+ size: EXCHANGE_SUB_SIZE,
609
+ 帯: { 幅: 六角の横幅(字下), 高さ: 14 },
610
+ },
611
+ ];
612
+ const 絵 = (倍率: number): JSX.Element => {
613
+ const 倍 = 打ち消しの倍率(倍率, 打ち消す字ら);
614
+ return (
553
615
  <g>
554
616
  {/* 8 dashed radial lines */}
555
617
  {nodes.map((n, i) => (
@@ -567,17 +629,18 @@ export function ShapeExchangeNode({ node, active, lang }: ShapeProps): JSX.Eleme
567
629
  })}
568
630
  {/* 中央 hexagon EXCHANGE (単色 #255a7a、 縦向き正六角形) */}
569
631
  <path d={hexPath} fill="#255a7a" stroke={accentDarker} strokeWidth={frame.strokeWidth} strokeLinejoin="round" />
570
- <text x={cx} y={cy - 8} fontSize={10} fontWeight={700} letterSpacing={2} textAnchor="middle" fill="#e5c07b">{字引("exchange.sign")}</text>
632
+ <text data-cdl-counter-scaled="0" x={cx} y={cy - 10} fontSize={11} fontWeight={700} letterSpacing={1} textAnchor="middle" fill="#e5c07b">{字引("exchange.sign")}</text>
571
633
  {/* swap 記号 */}
572
- <text data-cdl-role="node-body" x={cx} y={cy + 12} fontSize={22} fontWeight={700} textAnchor="middle" fill="#e5c07b">⇄</text>
634
+ <text data-cdl-counter-scaled="0" data-cdl-role="node-body" x={cx} y={cy + 12} fontSize={22} fontWeight={700} textAnchor="middle" fill="#e5c07b">⇄</text>
573
635
  {/* clearing label */}
574
- <text x={cx} y={cy + 26} fontSize={8} fontWeight={700} textAnchor="middle" fill="#a4b5f2">{字引("exchange.sub")}</text>
636
+ <text data-cdl-counter-scaled="1" x={cx} y={cy + 28} fontSize={EXCHANGE_SUB_SIZE * 倍} fontWeight={700} textAnchor="middle" fill="#a4b5f2">{字引("exchange.sub")}</text>
575
637
  {/* 8 外周 node (buyer/seller) */}
576
638
  {nodes.map((n, i) => (
577
639
  <circle key={`n${i}`} cx={n.cx} cy={n.cy} r={8} fill={n.fill} stroke={accentDarker} strokeWidth={1.5} />
578
640
  ))}
579
641
  </g>
580
- );
642
+ );
643
+ };
581
644
  return (
582
645
  <g>
583
646
  {fitArt(r, 広がり, 絵)}
@@ -607,12 +670,22 @@ const ATM_SHADOW_RX_RATIO = 0.6;
607
670
  const ATM_FLOOR_UP = 6;
608
671
  const ATM_FLOOR_H = 10;
609
672
  const ATM_FLOOR_RESERVE = 12;
610
- /** 操作面の自然な大きさ。 これより本体が小さければ丸ごと縮める。 */
611
- const ATM_PANEL_W = 140;
673
+ /**
674
+ * 操作面の自然な大きさ。 これより本体が小さければ丸ごと縮める。
675
+ *
676
+ * 幅は **画面の字から決める** (#888)。 画面でいちばん長い `SELECT AMOUNT` は指定 11 で
677
+ * 幅 119.6 になり、画面の内側 (`ATM_PANEL_W - 16`) から左右 4 ずつ空ける分を足すと 136 要る。
678
+ */
679
+ const ATM_PANEL_W = 152;
612
680
  const ATM_PANEL_H = 146;
613
- /** 全体の幅。 上限は今までと同じ 140。 */
681
+ /**
682
+ * 全体の幅。
683
+ *
684
+ * 上限を操作面と同じにする = 上限が操作面より狭いと `面` が 1 を切り、操作面の字が
685
+ * どんな箱でも指定より小さく描かれる (#888)。
686
+ */
614
687
  const ATM_W_MIN = 100;
615
- const ATM_W_MAX = 140;
688
+ const ATM_W_MAX = ATM_PANEL_W;
616
689
  /** 全体の高さの下限。 看板 + 間 + 本体 30 + 床帯。 */
617
690
  const ATM_H_MIN = ATM_SIGN_H + ATM_SIGN_GAP + 30 + ATM_FLOOR_RESERVE;
618
691
 
@@ -654,21 +727,30 @@ export function ShapeAtmNode({ node, active, lang }: ShapeProps): JSX.Element {
654
727
  // 画面と 10 キーが潰れて読めなくなる
655
728
  const 面 = Math.min(1, atmW / ATM_PANEL_W, atmH / ATM_PANEL_H);
656
729
  const 面左 = -ATM_PANEL_W / 2;
730
+ /*
731
+ * **操作面の字は打ち消さない** (#887 / #888)。 打ち消しの倍率は絵に 1 つで、いちばん余白の
732
+ * 無い字が全体の上限を決める。 鍵盤は 18x10 の鍵に数字を載せるため余白が無く
733
+ * (10 * 0.8 / 7 = 1.14 に対し幅は 18 / 字の幅)、鍵を含む操作面では上限が 1 前後に張り付く。
734
+ * 乗せても倍率が動かないので、印だけ付けて空回りさせない。
735
+ *
736
+ * 画面の 3 行とカード投入口の札は #888 で指定 11 に上げた。 行の間隔は 14 で、
737
+ * 指定 11 の字 (上 7.9 / 下 2.8) が上下の行にも画面の枠にも触れない。
738
+ */
657
739
  const 絵 = (
658
740
  <g>
659
741
  {/* drop shadow */}
660
742
  <ellipse cx={cx} cy={y + totalH + ATM_SHADOW_DY} rx={totalW * ATM_SHADOW_RX_RATIO} ry={ATM_SHADOW_RY} fill={frame.stroke} opacity={0.15} stroke="none" />
661
743
  {/* 天井 ATM CORNER 看板 */}
662
744
  <rect x={centerX - totalW / 2} y={signY} width={totalW} height={signH} fill={accentDarker} stroke="#000" strokeWidth={ATM_SIGN_SW} />
663
- <text x={centerX} y={signY + 17} fontSize={11} fontWeight={700} letterSpacing={2} textAnchor="middle" fill="#e5c07b">{字引("atm.sign")}</text>
745
+ <text data-cdl-counter-scaled="0" x={centerX} y={signY + 17} fontSize={11} fontWeight={700} letterSpacing={2} textAnchor="middle" fill="#e5c07b">{字引("atm.sign")}</text>
664
746
  {/* ATM body */}
665
747
  <rect data-cdl-role="node-body" x={atmX} y={atmY} width={atmW} height={atmH} rx={6} fill="#255a7a" stroke={accentDarker} strokeWidth={frame.strokeWidth} />
666
748
  <g transform={`translate(${centerX} ${atmY}) scale(${面})`}>
667
749
  {/* screen */}
668
750
  <rect x={面左 + 8} y={10} width={ATM_PANEL_W - 16} height={52} rx={3} fill="#0f1830" stroke="#000" strokeWidth={1.5} />
669
- <text x={0} y={30} fontSize={10} fontWeight={700} letterSpacing={1} textAnchor="middle" fill="#e5c07b">お引出し</text>
670
- <text x={0} y={44} fontSize={8} fontWeight={700} textAnchor="middle" fill="#a4b5f2">{字引("atm.screen")}</text>
671
- <text x={0} y={56} fontSize={10} fontWeight={700} textAnchor="middle" fill="#4ea36a">¥ 20,000</text>
751
+ <text data-cdl-counter-scaled="0" x={0} y={28} fontSize={11} fontWeight={700} letterSpacing={1} textAnchor="middle" fill="#e5c07b">お引出し</text>
752
+ <text data-cdl-counter-scaled="0" x={0} y={42} fontSize={11} fontWeight={700} textAnchor="middle" fill="#a4b5f2">{字引("atm.screen")}</text>
753
+ <text data-cdl-counter-scaled="0" x={0} y={56} fontSize={11} fontWeight={700} textAnchor="middle" fill="#4ea36a">¥ 20,000</text>
672
754
  {/* keypad 5x2 */}
673
755
  <rect x={面左 + 8} y={68} width={ATM_PANEL_W - 16} height={42} rx={2} fill="#8b949e" stroke="#5a6270" strokeWidth={1} />
674
756
  {(() => {
@@ -679,7 +761,7 @@ export function ShapeAtmNode({ node, active, lang }: ShapeProps): JSX.Element {
679
761
  const keyW = 18;
680
762
  const keyH = 10;
681
763
  const keyGap = 4;
682
- const startX = 面左 + 14;
764
+ const startX = 面左 + (ATM_PANEL_W - (5 * keyW + 4 * keyGap)) / 2;
683
765
  return keys.map((k, i) => {
684
766
  const row = Math.floor(i / 5);
685
767
  const col = i % 5;
@@ -688,7 +770,7 @@ export function ShapeAtmNode({ node, active, lang }: ShapeProps): JSX.Element {
688
770
  return (
689
771
  <g key={`k${i}`}>
690
772
  <rect x={kx} y={ky} width={keyW} height={keyH} fill={k[1]} stroke="#0f1830" strokeWidth={0.8} />
691
- <text x={kx + keyW / 2} y={ky + 7} fontSize={/* 決める鍵 (末尾) は 2 字あるので小さく描く。 字は言語で変わるため位置で見分ける */ i === keys.length - 1 ? 6 : 7} fontWeight={700} textAnchor="middle" fill="#ffffff">{k[0]}</text>
773
+ <text data-cdl-counter-scaled="0" x={kx + keyW / 2} y={ky + 7} fontSize={/* 決める鍵 (末尾) は 2 字あるので小さく描く。 字は言語で変わるため位置で見分ける */ i === keys.length - 1 ? 6 : 7} fontWeight={700} textAnchor="middle" fill="#ffffff">{k[0]}</text>
692
774
  </g>
693
775
  );
694
776
  });
@@ -696,7 +778,7 @@ export function ShapeAtmNode({ node, active, lang }: ShapeProps): JSX.Element {
696
778
  {/* card slot */}
697
779
  <rect x={面左 + 12} y={116} width={ATM_PANEL_W - 24} height={8} rx={1.5} fill="#0f1830" stroke={accentDarker} strokeWidth={1} />
698
780
  <rect x={面左 + 15} y={118} width={ATM_PANEL_W - 30} height={4} rx={0.5} fill="#e5c07b" />
699
- <text x={0} y={132} fontSize={6} fontWeight={700} letterSpacing={1} textAnchor="middle" fill="#e5c07b">{字引("atm.cardSlot")}</text>
781
+ <text data-cdl-counter-scaled="0" x={0} y={132} fontSize={11} fontWeight={700} letterSpacing={1} textAnchor="middle" fill="#e5c07b">{字引("atm.cardSlot")}</text>
700
782
  {/* cash slot */}
701
783
  <rect x={面左 + 12} y={136} width={ATM_PANEL_W - 24} height={4} rx={1} fill="#0f1830" />
702
784
  </g>
@@ -334,7 +334,7 @@ export function ShapeNotaryNode({ node, active }: ShapeProps): JSX.Element {
334
334
  <line x1={chestX - 14} y1={chestY + 14} x2={chestX + 14} y2={chestY + 14} stroke="#8b949e" strokeWidth={1} />
335
335
  <line x1={chestX - 14} y1={chestY + 22} x2={chestX + 8} y2={chestY + 22} stroke="#8b949e" strokeWidth={1} />
336
336
  <line x1={chestX - 14} y1={chestY + 34} x2={chestX + 2} y2={chestY + 34} stroke="#0f3450" strokeWidth={1.5} />
337
- <text x={chestX + 6} y={chestY + 37} fontSize={8} fill="#0f3450" fontFamily="cursive" fontStyle="italic">
337
+ <text data-cdl-counter-scaled="0" x={chestX + 6} y={chestY + 37} fontSize={8} fill="#0f3450" fontFamily="cursive" fontStyle="italic">
338
338
  ✓
339
339
  </text>
340
340
  {/* 右下 印章 badge */}
@@ -342,7 +342,7 @@ export function ShapeNotaryNode({ node, active }: ShapeProps): JSX.Element {
342
342
  <g>
343
343
  <circle cx={0} cy={0} r={12} fill="#c15a4a" stroke="none" />
344
344
  <circle cx={0} cy={0} r={8} fill="none" stroke="#ffffff" strokeWidth={1.2} />
345
- <text x={0} y={4} fontFamily="Georgia, serif" fontSize={10} fontWeight={700} fill="#ffffff" textAnchor="middle">
345
+ <text data-cdl-counter-scaled="0" x={0} y={4} fontFamily="Georgia, serif" fontSize={10} fontWeight={700} fill="#ffffff" textAnchor="middle">
346
346
  印
347
347
  </text>
348
348
  </g>
@@ -420,7 +420,7 @@ export function ShapeCustomerServiceNode({ node, active }: ShapeProps): JSX.Elem
420
420
  {collarV(cx, cy, p.strokeColor)}
421
421
  {/* 体内 24H badge */}
422
422
  <rect x={chestX - 22} y={chestY + 5} width={44} height={22} rx={11} fill="#e5c07b" stroke="#a67c00" strokeWidth={2} />
423
- <text x={chestX} y={chestY + 20} fontSize={12} fontWeight={900} fill="#5a3a10" textAnchor="middle" letterSpacing={2}>
423
+ <text data-cdl-counter-scaled="0" x={chestX} y={chestY + 20} fontSize={12} fontWeight={900} fill="#5a3a10" textAnchor="middle" letterSpacing={2}>
424
424
  24H
425
425
  </text>
426
426
  {/* 右下 headset badge */}
@@ -520,9 +520,10 @@ export function ShapeKanbanCardNode({ node, active, lang }: ShapeProps): JSX.Ele
520
520
  const ID字 = 11;
521
521
  const ID字間 = 1;
522
522
  const PILL上 = 20;
523
- const PILL幅 = 54;
523
+ // 札の幅は状態の字から決める = 指定 11 の `IN PROG` は字間 1 で幅 62.4、丸い端に触れない余白を足す (#888)
524
+ const PILL幅 = 70;
524
525
  const PILL高 = 16;
525
- const PILL字 = 9;
526
+ const PILL字 = 11;
526
527
  const FOOTER下 = 22;
527
528
  const AVATAR中心Y = 4;
528
529
  const AVATAR半径 = 9;
package/src/lang.ts CHANGED
@@ -68,8 +68,14 @@ const en = {
68
68
  "paymentProvider.sign": "PAY",
69
69
  /** `shape-payment-provider` の状態の札 */
70
70
  "paymentProvider.status": "PROCESSING",
71
- /** `shape-brokerage` の株価の帯 */
72
- "brokerage.ticker": "▲NKY 38,240 ▲AAPL 194",
71
+ /**
72
+ * `shape-brokerage` の株価の帯。
73
+ *
74
+ * **1 銘柄に絞る** (#888)。 電光掲示板の内側は幅 136 しか無く、2 銘柄を指定 11 で描くと
75
+ * 幅 181 になってビルの外へ出る。 板が流れている雰囲気は帯そのもの (黒地に緑の等幅) が
76
+ * 担うので、銘柄を減らしても絵は変わらない。
77
+ */
78
+ "brokerage.ticker": "▲NKY 38,240",
73
79
  /** `shape-brokerage` の看板 */
74
80
  "brokerage.sign": "$ SECURITIES",
75
81
  /** `shape-exchange` の看板 */
@@ -284,7 +290,7 @@ const ja: Record<CdlTextKey, string> = {
284
290
  "trustBank.sign": "信託",
285
291
  "paymentProvider.sign": "決済",
286
292
  "paymentProvider.status": "処理中",
287
- "brokerage.ticker": "▲日経 38,240 ▲東証 2,710",
293
+ "brokerage.ticker": "▲日経 38,240",
288
294
  "brokerage.sign": "¥ 証券",
289
295
  "exchange.sign": "取引所",
290
296
  "exchange.sub": "清算",
@@ -500,7 +500,6 @@ export interface VisualValidationReport {
500
500
 
501
501
  const MIN_NODE_W = 80;
502
502
  const MIN_NODE_H = 40;
503
- const MIN_FONT_SIZE = 12;
504
503
  const ROW_FORMAT = /^[^:]+:\s*.+$/;
505
504
 
506
505
  function emptyCounts(): Record<VisualAxis, number> {
@@ -2007,10 +2006,6 @@ function runAxes(
2007
2006
  );
2008
2007
  }
2009
2008
  }
2010
- // edge label の font size は collisions.ts で BBox 計算済、 ここでは MIN_FONT_SIZE 違反のみ check
2011
- // (実装では cdl が 12px 以下を render することはないので no-op、 将来拡張用)
2012
- void MIN_FONT_SIZE;
2013
-
2014
2009
  // ───────────────────────────────────────────────────────────
2015
2010
  // Axis 4: row-format (rows[] format + placeholder 解決)
2016
2011
  //