@alfadocs/ui-kit-debug 0.72.0 → 0.72.2

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.
@@ -553,72 +553,72 @@ const je = "0 0 808 944", $e = { x: 404, y: 472 }, ut = {
553
553
  {
554
554
  id: "cheekbone",
555
555
  labelKey: "cheekbone",
556
- d: "M610.94 390.75C608.16 382.3 605.46 381.87 596.6 382.08C591.51 382.28 584.59 382.59 575.62 381.39C557.25 379.38 481.3 349.58 454.86 341.75C422 333.52 392.16 327.22 375.57 324.41C381.84 329.92 388.57 338.91 380.64 346.05C389.64 348.16 401.43 351.27 415.06 355.22C467.31 373.66 535.67 406.59 557.94 417.54C568.72 423.72 574.39 429.66 578.65 434.5C584.76 442.1 588.17 446.76 600.61 448.17C605.08 448.79 608.5 446.59 611.06 441.43C614.87 433.1 616.26 409.71 610.94 390.76Z",
557
- cx: 496,
558
- cy: 387
556
+ d: "M587.86 415.87C586.01 408.8 583.79 409.02 576.91 410.15C573.08 410.38 567.86 410.32 561.24 408.36C547.07 403.39 488.66 358.5 465.97 345.98C436.12 334.42 407.31 328.09 390.38 326.48C395.19 331.07 399.27 338.58 388.69 345.72C397.48 346.49 408.73 348.34 421.39 351.61C467.3 374.36 523.12 420.78 541.55 435.68C550.62 443.21 555.39 449.39 559.11 454.28C564.66 461.73 567.89 466.16 579.26 467.46C583.31 467.67 586.22 465.29 588.19 460.34C591.18 452.28 592.1 431.43 587.86 415.88Z",
557
+ cx: 490,
558
+ cy: 397
559
559
  },
560
560
  {
561
561
  id: "profileContour",
562
562
  labelKey: "profileContour",
563
- d: "M679.49 460.92C662.93 432.48 622.82 379.58 610.17 357.37C607.54 340.28 611.01 332.13 582.48 277.31C541.29 213.09 467.7 142.01 335.51 118.16C214.35 137.81 168.28 242.2 178.26 335.41C163.99 397.4 203.96 424.45 203.96 424.45L235.52 453C242.29 459.8 254.86 462.31 264.65 458.66L321.41 442.2C328.49 440.1 326.07 430.35 318.89 425.44C297.28 411.69 267.1 389.83 273 362.76C276.61 346.81 288 334.19 300.84 324.75C319.85 310.82 349.78 307.41 366.39 317.62C366.54 317.73 366.64 317.84 366.72 317.96C372.18 318.69 388.18 321.17 409.63 325.09C405.81 321.88 402.77 318.08 400.57 313.75C397.08 307.29 396.4 301.59 396.28 300.57C389.27 281.5 410.62 239.48 419.79 210.81L419.86 210.53C420.33 208.66 422.64 207.79 425.9 208.29C429.16 208.82 432.81 210.6 435.45 212.93L544.85 305.25C548.57 308.5 549.65 311.64 547.27 312.72C545.88 313.32 544.21 314.81 542.63 316.93C542.95 317.13 543.14 317.25 543.14 317.25L578.22 341.99C579.74 339.92 581.98 337.32 585.35 335.66C588.85 333.29 593.45 333.53 602.9 346.57C603.48 347.63 604.8 349.91 605.69 351.36C606.35 352.44 606.45 352.51 606.01 351.68C605.24 350.27 604.53 349 603.85 347.85C608.9 356.45 620.49 381.67 616.12 376.62C609.85 371.55 601.15 349.63 596.39 340.46C594.58 340.08 592.85 340.4 591.22 341.48C588.15 343.06 586.14 345.68 584.84 347.55L587.8 350.22C588.64 351.01 589.24 351.93 589.46 352.78C589.67 353.64 589.47 354.34 588.93 354.73L561.7 370.22C569.18 372.95 573.07 373.93 575.12 374.17C583.51 375.24 589.9 374.95 595.07 374.74C603.66 374.53 609.22 374.57 614.07 388.6C618.2 402.65 621.13 431.87 614.87 446.84C611.24 454.21 606 457.66 599.76 456.78C596.46 456.37 593.65 455.7 591.21 454.82C587.73 453.61 585.02 451.95 582.66 449.97C582.53 453.68 582.22 458.38 581.59 463.89C587.14 465.39 598.05 470.35 603.05 485.32C605.84 495.29 605.31 507.77 601.29 522.54C603.25 526.51 609.11 537.5 620.13 538.58C628.34 539.19 636.2 543.59 641.84 549.94C646.74 545.1 652.14 538.43 654.44 531.02C655.29 518.93 652.79 509.96 656.5 508.65C659.72 507.91 672 512.24 681.12 500.96C689.12 489.32 690.1 489.92 679.53 460.96Z",
564
- cx: 427,
565
- cy: 334
563
+ d: "M657.04 427.15C645.32 417.42 612.92 377.67 608.47 342.76C610.05 315.91 614.77 299.37 606.91 241.79C600.19 175.54 556.62 116.74 420.63 130.08C253.44 160.79 152.79 273.93 132.98 380.57C116.36 516.03 182.43 589.12 182.43 589.12L226.57 641.41C234.86 650.15 249.78 644.43 261.09 627.35L321.54 525.13C328.57 512.58 325.7 495.54 318.17 493.61C294.63 488.83 257.86 468.83 262.84 409.94C267.69 380.06 284 357.04 302.38 340.78C330.2 319.79 367.62 311.97 382.74 320.74C382.87 320.84 382.94 320.94 382.99 321.04C388.82 321.28 405.44 322.73 426.75 326.43C424.09 323.46 422.53 319.98 422.13 315.98C421.41 309.9 423.49 304.51 423.88 303.54C426.23 284.75 477.87 242.7 506.61 211.31L506.84 210.99C508.32 208.89 511.62 207.65 515.45 207.77C519.23 207.92 522.88 209.42 524.98 211.72L567.11 326.31C568.13 330.65 567.2 335.05 564.76 336.69C563.35 337.54 561.24 339.5 558.91 342.14C559.06 342.41 559.15 342.57 559.15 342.57L573.97 372.38C575.71 369.67 578.22 365.84 581.33 362.37C585.49 356.14 590.79 348.98 597.61 352.24C598.15 352.43 598.76 354.7 598.84 357.08C598.91 358.84 598.59 359.88 598.14 359.69C597.37 359.38 596.65 359.2 595.96 359.12C597.33 369.53 600.96 398.18 595.38 398C588.92 397.38 587.72 374.56 588.46 362.31C586.69 363.79 584.85 365.85 582.96 368.5C580.04 371.85 577.73 375.59 576.26 377.97L577.27 380.55C577.56 381.29 577.66 382.22 577.55 383.11C577.43 384 577.11 384.76 576.66 385.24L553.04 396.96C558.13 400.5 560.86 401.88 562.35 402.34C568.32 404.04 572.97 404.02 576.7 403.74C583.09 402.53 587.8 400.9 590.94 412.75C594.16 424.28 596.78 449.29 591.87 464.27C589.18 471.56 584.78 475.46 579.05 475.22C575.99 474.97 573.36 474.4 571.04 473.57C567.75 472.35 565.15 470.69 562.9 468.71C562.88 472.1 562.76 476.4 562.38 481.46C567.8 483.15 578.56 487.63 584.78 500.94C588.58 509.77 589.68 521.45 587.53 535.74C590.03 539.27 597.49 548.85 608.32 547.11C615.16 544.28 621.37 543.88 625.6 545.18C627.13 536.84 628.35 525.82 627.96 516.71C626.08 501.81 623.74 494.92 625.48 485.82C627.82 477.03 640.49 466.24 651.72 451.57C662.37 438.81 663.39 438.52 657.07 427.15Z",
564
+ cx: 390,
565
+ cy: 383
566
566
  },
567
567
  {
568
568
  id: "temple",
569
569
  labelKey: "temple",
570
- d: "M532.36 315.34C532.57 314.85 532.88 314.51 533.27 314.29C535.46 311.03 538.01 308.57 540.36 307.57C540.87 307.35 540.68 306.7 539.9 306.02L430.37 214.29C429.7 213.7 429 213.52 428.64 213.47C428.29 213.41 427.67 213.39 427.56 213.86L427.48 214.14C425.45 221.87 420.09 238.28 415.47 255.43C409.74 269.9 401.22 289.46 405.13 299.8L405.18 299.95L405.19 300.1C405.22 300.32 406.48 322.76 432.19 329.92C432.5 330.01 432.79 330.12 433.05 330.25C441.15 332.22 449.62 334.37 458.27 336.64C495.23 347.63 521.06 355.99 538.7 361.94C536.62 359.47 534.53 356.66 532.75 353.55C528.88 347.24 527.33 341.48 527.2 336.3C525.08 326.23 528.9 321.14 531.31 317.29C531.35 317.24 531.37 317.19 531.4 317.14C531.76 316.51 532.09 315.92 532.35 315.34Z",
571
- cx: 471,
572
- cy: 288
570
+ d: "M551.29 339.27C551.73 338.72 552.17 338.34 552.62 338.11C556.19 334.28 559.65 331.21 562.14 329.85C562.68 329.54 562.9 328.65 562.67 327.75L518.07 213.95C517.55 213.37 516.78 213.26 516.36 213.24C515.95 213.22 515.19 213.27 514.81 213.81L514.57 214.13C507.63 223.01 490.31 241.35 473.08 259.55C456.86 274.15 435.38 293.06 434.06 303.22L434.04 303.36L433.97 303.51C433.88 303.72 424.42 324.26 448.09 332.54C448.37 332.66 448.62 332.79 448.84 332.93C456.22 335.74 463.82 338.97 471.46 342.54C502.66 360.75 523.25 375.04 536.66 384.85C535.73 382.31 534.95 379.47 534.57 376.45C533.66 370.15 534.7 364.78 536.86 360.11C539.61 349.89 545.41 345.33 549.41 341.41C549.46 341.36 549.51 341.3 549.56 341.25C550.19 340.57 550.77 339.92 551.29 339.26Z",
571
+ cx: 494,
572
+ cy: 299
573
573
  },
574
574
  {
575
575
  id: "preauricularArea",
576
576
  labelKey: "preauricularArea",
577
- d: "M404.06 424.3C405.42 426.58 404.49 428.64 401.99 428.89L430.02 475.88C440.29 480.63 455.17 488.9 470.83 502.17C477.68 508.4 484.74 516.12 491.29 525.49C498.26 511.18 499.64 482.61 494.41 443.27C491.07 415.76 484.68 392.64 483.22 387.83C458.42 377.47 432.33 367.56 410.47 360.25C398.03 356.52 387.16 353.53 378.59 351.41C378.25 356.06 379.25 364.97 384.91 382.96C391.82 402.36 403.92 424.06 404.06 424.29Z",
578
- cx: 439,
579
- cy: 438
577
+ d: "M399.04 423.47C400.19 426.46 399.28 429.83 397.03 431.07L421.24 493.26C429.33 496.6 440.56 504.27 451.47 519.28C455.98 526.45 460.47 535.45 464.49 546.37C473.34 527.06 479.06 492.31 479.5 447.01C480.52 417.95 479.02 394.87 478.75 390.28C457.75 375.55 435.02 362.96 415.32 355.59C403.76 352.86 393.4 351.38 385.05 350.82C383.53 355.04 382.54 362.85 384.93 378.19C389.1 396.84 398.92 423.16 399.04 423.46Z",
578
+ cx: 432,
579
+ cy: 449
580
580
  },
581
581
  {
582
582
  id: "mandibularBody",
583
583
  labelKey: "mandibularBody",
584
- d: "M467.95 509.13C456.53 499.21 445.4 492.21 436.52 487.47L459.05 533.53L459.14 533.79C459.89 536.18 467.05 571.24 498.43 592.3C500.51 593.92 502.72 595.62 505 597.38C503.47 562.75 491.22 532.29 467.95 509.13Z",
585
- cx: 471,
586
- cy: 542
584
+ d: "M448.9 529.33C441.36 518.39 433.39 511.79 426.69 508.14L444.05 565.22L444.11 565.55C444.61 568.53 446.18 612.75 465.58 621.58C467.31 622.11 469.2 622.59 471.21 623.04C472.27 587.41 463.32 555.13 448.9 529.33Z",
585
+ cx: 449,
586
+ cy: 566
587
587
  },
588
588
  {
589
589
  id: "submentalProfile",
590
590
  labelKey: "submentalProfile",
591
- d: "M561.28 637.02C560.29 636.32 559.31 635.56 558.35 634.74C550.86 628.63 541.7 615.89 529.55 597.01C529.52 596.98 529.5 596.94 529.47 596.9C529.09 596.39 528.71 595.89 528.32 595.37C520.33 585.52 512.8 576.33 508.52 571.48C509.7 580.62 510.18 590.53 509.86 601.23C532.11 622.53 558.17 652.04 558.17 652.04C558.17 652.04 579.6 677.67 591.07 642.74C587.26 643.92 583.05 644.26 578.83 643.73C572.73 643.21 566.77 640.97 561.27 637.03Z",
592
- cx: 550,
593
- cy: 625
591
+ d: "M499.51 624.48C500.22 624.28 501.01 624.05 501.85 623.78C506.95 622.1 508.34 617.25 499.33 608.72C499.3 608.71 499.28 608.69 499.25 608.67C498.87 608.43 498.48 608.2 498.08 607.95C489.61 603.09 481.6 597.71 477.16 594.66C477.83 603.57 477.42 613.28 475.61 623.94C494.7 628.41 483.66 636.08 483.66 636.08C483.66 636.08 476.69 649.28 527.7 627.3C518.99 627.63 510.52 627.34 503.56 626.55C496.4 626.16 495.89 625.71 499.5 624.48Z",
592
+ cx: 502,
593
+ cy: 622
594
594
  },
595
595
  {
596
596
  id: "masseter",
597
597
  labelKey: "masseter",
598
- d: "M573.89 440.59C569.65 435.8 564.53 430.44 554.61 424.7C543.46 418.85 521.77 406.59 494.84 393.1C500.65 416.63 515.03 501.76 496.1 533.47C500.2 541.35 503.71 550.24 506.34 560.17C507 560.02 507.7 560.24 508.29 560.88C508.51 561.11 517.6 571.65 528.38 584.23C550.75 552.98 563.86 516.16 570.4 490.42C576.1 466.55 576.18 448.24 575.95 443.02C575.27 442.23 574.59 441.42 573.88 440.59Z",
599
- cx: 536,
600
- cy: 489
598
+ d: "M554.71 459.19C550.92 454.22 546.49 448.51 537.91 441.39C528.42 433.63 510.52 417.13 488.4 397.93C489.3 421.03 491.75 514.66 467.63 555.34C470.6 563.87 473.34 573.19 475.46 583.39C476.18 582.97 476.93 582.94 477.54 583.4C477.77 583.56 487.55 590.84 499.76 598.04C530.58 563.84 546.09 529.73 552.28 505.65C557.13 483.51 556.81 466.48 556.58 461.68C555.96 460.87 555.34 460.04 554.7 459.2Z",
599
+ cx: 512,
600
+ cy: 498
601
601
  },
602
602
  {
603
603
  id: "cheek",
604
604
  labelKey: "cheek",
605
- d: "M531.91 588.63L531.97 589.2C534.27 588.94 543.9 587.63 554.51 581.36C570.49 572.8 583.41 557.03 592.56 533.24C599.01 515.51 600.78 501.14 597.95 490.55C594.05 478.42 585.01 474.09 580.29 472.68C579.27 478.6 577.89 485.15 576.05 492.18C568.74 518.83 554.61 556.58 531.91 588.63Z",
606
- cx: 566,
607
- cy: 531
605
+ d: "M503.7 600.29L503.69 600.76C506.68 599.48 519.15 594.49 532.76 586.74C551.63 579.2 568.84 567.08 579.23 546.08C584.39 529.37 584.46 515.84 580.48 506.25C575.46 495.21 566.3 491.13 561.55 489.57C560.86 495.06 559.83 501.15 558.34 507.68C551.78 532.36 535.25 566.52 503.69 600.3Z",
606
+ cx: 544,
607
+ cy: 545
608
608
  },
609
609
  {
610
610
  id: "chin",
611
611
  labelKey: "chin",
612
- d: "M633.63 557.45C629.08 552.63 623.13 549.04 616.46 548.35C606.36 547.06 600.75 538.23 597.86 532.69C597.5 533.64 597.15 534.59 596.76 535.56C585.82 562.54 571.02 580.56 554.12 589.53C547.61 593.34 541.62 595.3 537.29 596.35C545.21 608.73 553.26 621.16 560.49 627.26C573.24 638.7 586.3 638.16 594.18 633.77C594.79 631.8 595.4 629.7 596 627.43L605.24 597.03L605.32 596.82C605.67 595.83 608.76 587.94 612.99 586.93C617.67 585.64 629.4 566.81 632.93 558.71C633.12 558.27 633.35 557.86 633.61 557.47Z",
613
- cx: 585,
614
- cy: 586
612
+ d: "M622.37 557.72C618.53 556.38 613.08 555.94 606.76 557.59C595.83 558.3 588.65 550.46 584.99 545.44C584.69 546.34 584.38 547.24 584.03 548.15C571.22 572.11 550.01 585.32 530.6 593C522.53 597.65 514.89 601.46 509.24 604.26C515.21 610.11 514.33 615.81 510.02 618.18C502.34 623.58 523.79 624.61 543.95 623.09C547.34 622.17 550.83 621.19 554.45 620.12L594.99 602.7L595.21 602.54C596.13 601.8 602.57 595.37 608.29 593.49C613.07 590.42 621.01 568.35 622.15 559.26C622.19 558.76 622.27 558.26 622.36 557.75Z",
613
+ cx: 562,
614
+ cy: 585
615
615
  },
616
616
  {
617
617
  id: "browTail",
618
618
  labelKey: "browTail",
619
- d: "M540.86 320.68C540.86 320.68 520.89 338.47 552.84 365.63L580.98 350.28L540.86 320.68Z",
620
- cx: 551,
621
- cy: 343
619
+ d: "M555.64 346.57C555.64 346.57 530.25 360.36 547.29 391.38L572.95 381.28L555.64 346.57Z",
620
+ cx: 552,
621
+ cy: 369
622
622
  }
623
623
  ], at = mt.find((t) => t.id === "profileContour") ?? ut, it = mt.filter(
624
624
  (t) => t.id !== "profileContour"
@@ -1447,4 +1447,4 @@ export {
1447
1447
  Bt as j,
1448
1448
  ft as s
1449
1449
  };
1450
- //# sourceMappingURL=face-scheme-DOZO3M_c.js.map
1450
+ //# sourceMappingURL=face-scheme-BT9_KdL0.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"face-scheme-BT9_KdL0.js","sources":["../../src/components/face-scheme/face-scheme.agent.ts","../../src/components/face-scheme/detect-face-landmarks.ts","../../src/components/face-scheme/detect-face-yunet.ts","../../src/components/face-scheme/face-scheme-zones.ts","../../src/components/face-scheme/face-scheme-data.ts","../../src/components/face-scheme/face-scheme.tsx"],"sourcesContent":["import type { AgentAdapter } from '../../agent/types';\nimport type { FaceSchemeHandle } from './face-scheme';\nimport type { FaceFindingType, FaceViewId } from './face-scheme-data';\n\nconst ZONE_SCHEMA = {\n type: 'string',\n description:\n \"Face-zone id (e.g. 'glabella', 'nasolabialFold', 'forehead', 'mandibularAngleLeft' on the front view; 'cheekbone', 'masseter', 'chin' on a profile view).\",\n} as const;\n\nconst VIEW_SCHEMA = {\n type: 'string',\n enum: ['front', 'profile-left', 'profile-right'],\n description: \"Active view id: 'front', 'profile-left', or 'profile-right'.\",\n} as const;\n\nconst FINDING_SCHEMA = {\n enum: ['observation', 'treatment', 'concern', 'note'],\n} as const;\n\nexport const faceSchemeAgent: AgentAdapter<FaceSchemeHandle> = {\n id: 'face-scheme',\n // pick — focus a zone (transient); set_value / edit_inline — record / clear\n // findings on a zone; view_change — switch the active view (front / left /\n // right profile), since each view renders a different zone set. Registration\n // (the photo fit) is exposed only as the auto-align action.\n capabilities: ['pick', 'set_value', 'edit_inline', 'view_change'],\n state: {\n activeView: {\n // Which view is mounted — it determines which zones are in the DOM, so an\n // agent must read this before focus_zone on a profile-only zone.\n type: 'string',\n descriptionKey: 'ui.agent.faceScheme.state.activeView',\n description:\n 'Id of the currently displayed view (front / profile-left / profile-right).',\n read: (handle) => handle.getView(),\n },\n chart: {\n // A9: identifiers + finding ENUMS only — never the free-text finding\n // `note` (PHI) and never the patient-photo `src`. Notes are written but\n // never read back through the snapshot.\n type: 'Record<string, string[]>',\n descriptionKey: 'ui.agent.faceScheme.state.chart',\n description:\n 'Current face chart — the finding types recorded on each zone, keyed by zone id. Free-text notes and the patient photo are not exposed here.',\n read: (handle) =>\n Object.fromEntries(\n Object.entries(handle.getChart()).map(([zone, findings]) => [\n zone,\n findings.map((f) => f.type),\n ]),\n ),\n },\n markedZones: {\n type: 'string[]',\n descriptionKey: 'ui.agent.faceScheme.state.markedZones',\n description: 'Zone ids carrying at least one finding.',\n read: (handle) => Object.keys(handle.getChart()),\n },\n },\n actions: {\n focus_zone: {\n safety: 'read',\n argsType: '{ id: string }',\n argsSchema: {\n type: 'object',\n properties: { id: ZONE_SCHEMA },\n required: ['id'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.faceScheme.actions.focusZone',\n description: 'Move focus to a specific face zone by id.',\n invoke: (handle, args: { id: string }) => {\n handle.focusZone(args.id);\n },\n },\n get_zone: {\n safety: 'read',\n argsType: '{ id: string }',\n argsSchema: {\n type: 'object',\n properties: { id: ZONE_SCHEMA },\n required: ['id'],\n },\n returns: 'string[]',\n returnsSchema: { type: 'array', items: FINDING_SCHEMA },\n idempotent: true,\n descriptionKey: 'ui.agent.faceScheme.actions.getZone',\n description:\n 'Read the finding types recorded on one zone (notes are not returned).',\n invoke: (handle, args: { id: string }) =>\n handle.getZone(args.id).map((f) => f.type),\n },\n zones_with: {\n safety: 'read',\n argsType: '{ finding: FaceFindingType }',\n argsSchema: {\n type: 'object',\n properties: { finding: FINDING_SCHEMA },\n required: ['finding'],\n },\n returns: 'string[]',\n returnsSchema: { type: 'array', items: ZONE_SCHEMA },\n idempotent: true,\n descriptionKey: 'ui.agent.faceScheme.actions.zonesWith',\n description: 'List the zone ids carrying a given finding type.',\n invoke: (handle, args: { finding: FaceFindingType }) =>\n handle.zonesWith(args.finding),\n },\n set_finding: {\n safety: 'write',\n argsType: '{ id: string; finding: FaceFindingType; note?: string }',\n argsSchema: {\n type: 'object',\n properties: {\n id: ZONE_SCHEMA,\n finding: FINDING_SCHEMA,\n note: { type: 'string' },\n },\n required: ['id', 'finding'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.faceScheme.actions.setFinding',\n description:\n 'Record a finding on a zone, with an optional free-text note.',\n invoke: (\n handle,\n args: { id: string; finding: FaceFindingType; note?: string },\n ) => {\n handle.setFinding(args.id, args.finding, args.note);\n },\n },\n remove_finding: {\n safety: 'write',\n argsType: '{ id: string; finding: FaceFindingType }',\n argsSchema: {\n type: 'object',\n properties: { id: ZONE_SCHEMA, finding: FINDING_SCHEMA },\n required: ['id', 'finding'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.faceScheme.actions.removeFinding',\n description: 'Remove a single finding from a zone.',\n invoke: (handle, args: { id: string; finding: FaceFindingType }) => {\n handle.removeFinding(args.id, args.finding);\n },\n },\n clear_zone: {\n safety: 'destructive',\n argsType: '{ id: string }',\n argsSchema: {\n type: 'object',\n properties: { id: ZONE_SCHEMA },\n required: ['id'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.faceScheme.actions.clearZone',\n description: 'Clear every finding from a zone.',\n invoke: (handle, args: { id: string }) => {\n handle.clearZone(args.id);\n },\n },\n auto_align: {\n safety: 'write',\n argsType: '{}',\n argsSchema: { type: 'object', properties: {} },\n idempotent: true,\n descriptionKey: 'ui.agent.faceScheme.actions.autoAlign',\n description:\n 'Run the on-device detector to align the zone overlay to the patient photo (works on the active view — front or profile). No-op if detection is disabled or no face is found.',\n invoke: (handle) => {\n void handle.detect();\n },\n },\n set_view: {\n safety: 'write',\n argsType: '{ view: FaceViewId }',\n argsSchema: {\n type: 'object',\n properties: { view: VIEW_SCHEMA },\n required: ['view'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.faceScheme.actions.setView',\n description:\n \"Switch the active view ('front' / 'profile-left' / 'profile-right'). Each view renders a different zone set, so switch before focusing a zone that belongs to another view.\",\n invoke: (handle, args: { view: FaceViewId }) => {\n handle.setView(args.view);\n },\n },\n },\n domHooks: {\n root: { attr: 'data-component', value: 'face-scheme' },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n item: {\n attr: 'data-zone',\n description:\n 'Each clickable zone `<g>` carries its zone id as `data-zone` (plus `data-side` for the anatomical side). The root `<div>` carries the active view id as `data-view` — only the zones for the active view are in the DOM.',\n },\n },\n};\n","/* ------------------------------------------------------------------ */\n/* FaceScheme — on-device face-landmark detection (lazy). */\n/* */\n/* Wraps Google's MediaPipe Face Landmarker. The model + wasm runtime */\n/* are LAZY-LOADED from a CDN (overridable) only when detection actually */\n/* runs — so the kit's base bundle ships ZERO ML and FaceScheme stays */\n/* lightweight for consumers that never auto-detect. Everything runs */\n/* on-device: the patient photo and the derived landmarks never leave */\n/* the browser. The same helper is exported for consumer-side use. */\n/* */\n/* It returns canonical-anchor → photo-point pairs in the FaceScheme */\n/* view coordinate space, ready to hand to `FaceSchemeHandle.fitToLand- */\n/* marks` (or the kit feeds them internally for Auto-fit). */\n/* ------------------------------------------------------------------ */\n\nimport type { Point } from './face-scheme-data';\n\n/** Default CDN sources — override to self-host (recommended for production CSP). */\nconst DEFAULT_VISION_MODULE =\n 'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.18/vision_bundle.mjs';\nconst DEFAULT_WASM_BASE =\n 'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.18/wasm';\nconst DEFAULT_MODEL =\n 'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task';\n\n/**\n * Canonical FaceScheme anchor → MediaPipe FaceMesh landmark indices.\n * Multiple indices are averaged. Sides are anatomical (the subject's right eye\n * sits on the viewer's left), matching the canonical anchor ids in the data\n * module. Indices are the stable FaceMesh topology (468/478-point).\n */\nconst ANCHOR_INDICES: Record<string, number[]> = {\n 'right-eye': [33, 133], // subject's right eye, outer + inner corners\n 'left-eye': [263, 362], // subject's left eye, outer + inner corners\n 'nose-tip': [1],\n chin: [152],\n // Midline cephalometric points — the profile views fit on these (the detector\n // tracks them on a side profile even though the contralateral side is\n // occluded). The front view ignores ids it has no landmark for.\n nasion: [168], // bridge of the nose, between the brows\n subnasale: [2], // base of the nose / top of the philtrum\n};\n\nexport interface DetectFaceLandmarksOptions {\n /** View coordinate width the points must be projected into (the SVG viewBox W). */\n viewWidth: number;\n /** View coordinate height (the SVG viewBox H). */\n viewHeight: number;\n /** ESM bundle URL for `@mediapipe/tasks-vision`. */\n visionModuleUrl?: string;\n /** Base URL for the MediaPipe wasm fileset. */\n wasmBaseUrl?: string;\n /** URL of the `face_landmarker.task` model. */\n modelAssetPath?: string;\n /** Lifecycle hook — `'loading'` while the model initialises, `'detecting'` during inference. */\n onStage?: (stage: 'loading' | 'detecting') => void;\n}\n\ninterface NormalizedLandmark {\n x: number;\n y: number;\n z: number;\n}\ninterface FaceLandmarkerResult {\n faceLandmarks: NormalizedLandmark[][];\n}\ninterface FaceLandmarkerInstance {\n detect: (image: HTMLImageElement) => FaceLandmarkerResult;\n}\ninterface VisionModule {\n FilesetResolver: { forVisionTasks: (wasmBase: string) => Promise<unknown> };\n FaceLandmarker: {\n createFromOptions: (\n fileset: unknown,\n options: {\n baseOptions: { modelAssetPath: string; delegate?: 'CPU' | 'GPU' };\n runningMode: 'IMAGE' | 'VIDEO';\n numFaces: number;\n },\n ) => Promise<FaceLandmarkerInstance>;\n };\n}\n\n// One landmarker per unique source triple, loaded once and reused.\nconst landmarkerCache = new Map<string, Promise<FaceLandmarkerInstance>>();\n\nfunction getLandmarker(\n visionModuleUrl: string,\n wasmBaseUrl: string,\n modelAssetPath: string,\n onStage?: (stage: 'loading' | 'detecting') => void,\n): Promise<FaceLandmarkerInstance> {\n const key = `${visionModuleUrl}|${wasmBaseUrl}|${modelAssetPath}`;\n const cached = landmarkerCache.get(key);\n if (cached) return cached;\n onStage?.('loading');\n // Variable specifier + @vite-ignore: nothing is bundled or pre-resolved; the\n // ESM module is fetched at call time. Cast through `unknown` — no `any`.\n const promise = (async () => {\n const mod = (await import(\n /* @vite-ignore */ visionModuleUrl\n )) as unknown as VisionModule;\n const fileset = await mod.FilesetResolver.forVisionTasks(wasmBaseUrl);\n return mod.FaceLandmarker.createFromOptions(fileset, {\n baseOptions: { modelAssetPath, delegate: 'GPU' },\n runningMode: 'IMAGE',\n numFaces: 1,\n });\n })();\n landmarkerCache.set(key, promise);\n // Don't cache a rejected init — let the next call retry.\n promise.catch(() => landmarkerCache.delete(key));\n return promise;\n}\n\nfunction loadImage(src: string): Promise<HTMLImageElement> {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.src = src;\n return img.decode().then(() => img);\n}\n\n/**\n * Detect a single face in `src` and return the FaceScheme canonical-anchor →\n * photo-point pairs, projected into the view coordinate space using the same\n * `xMidYMid slice` (cover) fit the FaceScheme `<image>` uses. Resolves `null`\n * if no face is found. Browser-only.\n */\nexport async function detectFaceLandmarks(\n src: string,\n options: DetectFaceLandmarksOptions,\n): Promise<Array<{ id: string; photo: Point }> | null> {\n if (typeof window === 'undefined' || typeof Image === 'undefined') {\n throw new Error('detectFaceLandmarks runs in the browser only.');\n }\n const {\n viewWidth,\n viewHeight,\n visionModuleUrl = DEFAULT_VISION_MODULE,\n wasmBaseUrl = DEFAULT_WASM_BASE,\n modelAssetPath = DEFAULT_MODEL,\n onStage,\n } = options;\n\n const [image, landmarker] = await Promise.all([\n loadImage(src),\n getLandmarker(visionModuleUrl, wasmBaseUrl, modelAssetPath, onStage),\n ]);\n\n onStage?.('detecting');\n const result = landmarker.detect(image);\n const face = result.faceLandmarks[0];\n if (!face || face.length === 0) return null;\n\n // Replicate the component's cover (slice) placement: the image fills the\n // viewBox, centred, cropped — map normalized landmarks → view coordinates.\n const imgW = image.naturalWidth || image.width;\n const imgH = image.naturalHeight || image.height;\n const scale = Math.max(viewWidth / imgW, viewHeight / imgH);\n const offsetX = (viewWidth - imgW * scale) / 2;\n const offsetY = (viewHeight - imgH * scale) / 2;\n const toView = (lm: NormalizedLandmark): Point => ({\n x: offsetX + lm.x * imgW * scale,\n y: offsetY + lm.y * imgH * scale,\n });\n\n const pairs: Array<{ id: string; photo: Point }> = [];\n for (const [id, indices] of Object.entries(ANCHOR_INDICES)) {\n let sx = 0;\n let sy = 0;\n let count = 0;\n for (const idx of indices) {\n const lm = face[idx];\n if (!lm) continue;\n const v = toView(lm);\n sx += v.x;\n sy += v.y;\n count++;\n }\n if (count > 0) pairs.push({ id, photo: { x: sx / count, y: sy / count } });\n }\n return pairs.length >= 3 ? pairs : null;\n}\n","/* ------------------------------------------------------------------ */\n/* FaceScheme — on-device PROFILE face detection (lazy). */\n/* */\n/* The MediaPipe FaceMesh family (see detect-face-landmarks.ts) is */\n/* trained near-frontal: on a 90° lateral profile it under-reads the */\n/* pose and hallucinates the occluded half of the face, so it can't */\n/* register the profile mesh. YuNet (OpenCV Zoo) is a WIDERFACE-trained */\n/* CNN detector that tracks the VISIBLE-side keypoints across poses, so */\n/* the kit uses it for the two profile views. */\n/* */\n/* Runtime: onnxruntime-web is LAZY-LOADED from a CDN (overridable) and */\n/* the ~232 KB model is a code-split asset fetched only when profile */\n/* auto-fit actually runs — the base bundle ships zero ML, matching the */\n/* MediaPipe wrapper. Everything runs on-device: the patient photo and */\n/* the derived keypoints never leave the browser. */\n/* */\n/* It returns YuNet's five canonical keypoints (two eyes, nose tip, two */\n/* mouth corners) projected into the FaceScheme view coordinate space. */\n/* The component picks the visible-side eye + mouth for the profile fit. */\n/* ------------------------------------------------------------------ */\n\nimport type { Point } from './face-scheme-data';\n\n/** Default CDN sources — override to self-host (recommended for production CSP). */\nconst DEFAULT_ORT_MODULE =\n 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.19.2/dist/ort.min.mjs';\nconst DEFAULT_ORT_WASM_BASE =\n 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.19.2/dist/';\n\n/** YuNet 2023mar: fixed 640×640 input, decoded over three strides. */\nconst NET_SIZE = 640;\nconst STRIDES = [8, 16, 32] as const;\n/** YuNet keypoint order → FaceScheme anchor ids (sides are the subject's). */\nconst KEYPOINT_IDS = [\n 'right-eye',\n 'left-eye',\n 'nose-tip',\n 'mouth-right',\n 'mouth-left',\n] as const;\n\nexport interface DetectFaceYuNetOptions {\n /** View coordinate width the points must be projected into (the SVG viewBox W). */\n viewWidth: number;\n /** View coordinate height (the SVG viewBox H). */\n viewHeight: number;\n /** ESM bundle URL for `onnxruntime-web`. */\n ortModuleUrl?: string;\n /** Base URL for the ORT wasm fileset. */\n ortWasmBaseUrl?: string;\n /** URL of the YuNet `.onnx` model (defaults to the kit's bundled copy). */\n modelUrl?: string;\n /** Minimum detection score (default 0.6). */\n scoreThreshold?: number;\n /** Lifecycle hook — `'loading'` while the runtime/model initialise, `'detecting'` during inference. */\n onStage?: (stage: 'loading' | 'detecting') => void;\n}\n\ninterface OrtTensor {\n data: Float32Array;\n dims: readonly number[];\n}\ninterface OrtSession {\n inputNames: string[];\n run: (feeds: Record<string, OrtTensor>) => Promise<Record<string, OrtTensor>>;\n}\ninterface OrtModule {\n env: { wasm: { wasmPaths: string; numThreads: number }; logLevel?: string };\n Tensor: new (\n type: 'float32',\n data: Float32Array,\n dims: number[],\n ) => OrtTensor;\n InferenceSession: {\n create: (\n model: Uint8Array,\n options: { executionProviders: string[] },\n ) => Promise<OrtSession>;\n };\n}\n\n// One session per unique source pair, loaded once and reused.\nconst sessionCache = new Map<\n string,\n Promise<{ ort: OrtModule; session: OrtSession }>\n>();\n\nasync function getSession(\n ortModuleUrl: string,\n ortWasmBaseUrl: string,\n modelUrl: string,\n onStage?: (stage: 'loading' | 'detecting') => void,\n): Promise<{ ort: OrtModule; session: OrtSession }> {\n const key = `${ortModuleUrl}|${ortWasmBaseUrl}|${modelUrl}`;\n const cached = sessionCache.get(key);\n if (cached) return cached;\n onStage?.('loading');\n // Variable specifier + @vite-ignore: nothing is bundled or pre-resolved; the\n // ESM runtime is fetched at call time. Cast through `unknown` — no `any`.\n const promise = (async () => {\n const ort = (await import(\n /* @vite-ignore */ ortModuleUrl\n )) as unknown as OrtModule;\n ort.env.wasm.wasmPaths = ortWasmBaseUrl;\n // Single-threaded: avoids the cross-origin-isolation (COOP/COEP) requirement\n // that SharedArrayBuffer-backed threads impose on the host page.\n ort.env.wasm.numThreads = 1;\n ort.env.logLevel = 'error';\n const model = new Uint8Array(await (await fetch(modelUrl)).arrayBuffer());\n const session = await ort.InferenceSession.create(model, {\n executionProviders: ['wasm'],\n });\n return { ort, session };\n })();\n sessionCache.set(key, promise);\n promise.catch(() => sessionCache.delete(key));\n return promise;\n}\n\nfunction loadImage(src: string): Promise<HTMLImageElement> {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.src = src;\n return img.decode().then(() => img);\n}\n\n/** Resolve the kit's bundled model URL (code-split — fetched only on demand). */\nasync function defaultModelUrl(): Promise<string> {\n const mod = (await import('./face_detection_yunet_2023mar.onnx?url')) as {\n default: string;\n };\n return mod.default;\n}\n\ninterface Candidate {\n score: number;\n keypoints: Array<[number, number]>;\n}\n\n/** Letterbox into NET_SIZE², BGR, NCHW, 0–255 (YuNet/OpenCV convention). */\nfunction preprocess(\n Tensor: OrtModule['Tensor'],\n img: HTMLImageElement,\n): { tensor: OrtTensor; scale: number; padX: number; padY: number } {\n const w = img.naturalWidth || img.width;\n const h = img.naturalHeight || img.height;\n const scale = Math.min(NET_SIZE / w, NET_SIZE / h);\n const dw = Math.round(w * scale);\n const dh = Math.round(h * scale);\n const padX = Math.floor((NET_SIZE - dw) / 2);\n const padY = Math.floor((NET_SIZE - dh) / 2);\n const canvas = document.createElement('canvas');\n canvas.width = NET_SIZE;\n canvas.height = NET_SIZE;\n const ctx = canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context unavailable.');\n // Letterbox padding is black: a fresh canvas's default fillStyle is already\n // opaque black, so fillRect needs no colour (YuNet expects zero-padding).\n ctx.fillRect(0, 0, NET_SIZE, NET_SIZE);\n ctx.drawImage(img, padX, padY, dw, dh);\n const { data } = ctx.getImageData(0, 0, NET_SIZE, NET_SIZE);\n const n = NET_SIZE * NET_SIZE;\n const f = new Float32Array(3 * n);\n for (let i = 0; i < n; i++) {\n f[i] = data[i * 4 + 2]; // B\n f[n + i] = data[i * 4 + 1]; // G\n f[2 * n + i] = data[i * 4]; // R\n }\n return {\n tensor: new Tensor('float32', f, [1, 3, NET_SIZE, NET_SIZE]),\n scale,\n padX,\n padY,\n };\n}\n\nconst sigmoid = (x: number): number => 1 / (1 + Math.exp(-x));\n\n/** Decode YuNet's 12 outputs (cls/obj/bbox/kps × 3 strides) → keypoint sets. */\nfunction decode(\n out: Record<string, OrtTensor>,\n scoreThreshold: number,\n): Candidate | null {\n // 2023mar bakes the sigmoid in; older exports emit logits — detect + apply.\n let needSigmoid = false;\n const probe = out['cls_8'].data;\n for (let i = 0; i < Math.min(probe.length, 256); i++) {\n if (probe[i] < 0 || probe[i] > 1) {\n needSigmoid = true;\n break;\n }\n }\n let best: Candidate | null = null;\n for (const s of STRIDES) {\n const cls = out[`cls_${s}`].data;\n const obj = out[`obj_${s}`].data;\n const kps = out[`kps_${s}`].data;\n const cols = NET_SIZE / s;\n const rows = NET_SIZE / s;\n const n = cols * rows;\n for (let i = 0; i < n; i++) {\n let cv = cls[i];\n let ov = obj[i];\n if (needSigmoid) {\n cv = sigmoid(cv);\n ov = sigmoid(ov);\n }\n cv = Math.min(Math.max(cv, 0), 1);\n ov = Math.min(Math.max(ov, 0), 1);\n const score = Math.sqrt(cv * ov);\n if (score < scoreThreshold) continue;\n if (best && score <= best.score) continue;\n const c = i % cols;\n const r = Math.floor(i / cols);\n const keypoints: Array<[number, number]> = [];\n for (let j = 0; j < 5; j++) {\n keypoints.push([\n (c + kps[i * 10 + 2 * j]) * s,\n (r + kps[i * 10 + 2 * j + 1]) * s,\n ]);\n }\n best = { score, keypoints };\n }\n }\n return best;\n}\n\n/**\n * Detect a single face in `src` and return YuNet's canonical keypoint →\n * photo-point pairs, projected into the view coordinate space using the same\n * `xMidYMid slice` (cover) fit the FaceScheme `<image>` uses. Resolves `null`\n * if no face is found. Browser-only.\n */\nexport async function detectFaceYuNet(\n src: string,\n options: DetectFaceYuNetOptions,\n): Promise<Array<{ id: string; photo: Point }> | null> {\n if (typeof window === 'undefined' || typeof Image === 'undefined') {\n throw new Error('detectFaceYuNet runs in the browser only.');\n }\n const {\n viewWidth,\n viewHeight,\n ortModuleUrl = DEFAULT_ORT_MODULE,\n ortWasmBaseUrl = DEFAULT_ORT_WASM_BASE,\n modelUrl = await defaultModelUrl(),\n scoreThreshold = 0.6,\n onStage,\n } = options;\n\n const [image, { ort, session }] = await Promise.all([\n loadImage(src),\n getSession(ortModuleUrl, ortWasmBaseUrl, modelUrl, onStage),\n ]);\n\n onStage?.('detecting');\n const { tensor, scale, padX, padY } = preprocess(ort.Tensor, image);\n const result = await session.run({ [session.inputNames[0]]: tensor });\n const face = decode(result, scoreThreshold);\n if (!face) return null;\n\n // Replicate the component's cover (slice) placement of the <image>.\n const imgW = image.naturalWidth || image.width;\n const imgH = image.naturalHeight || image.height;\n const coverScale = Math.max(viewWidth / imgW, viewHeight / imgH);\n const offsetX = (viewWidth - imgW * coverScale) / 2;\n const offsetY = (viewHeight - imgH * coverScale) / 2;\n\n return face.keypoints.map(([kx, ky], j) => {\n // Un-letterbox (network space → original image px), then project to view.\n const px = (kx - padX) / scale;\n const py = (ky - padY) / scale;\n return {\n id: KEYPOINT_IDS[j],\n photo: {\n x: offsetX + px * coverScale,\n y: offsetY + py * coverScale,\n },\n };\n });\n}\n","/* Provenance — FRONT zones are AUTO-GENERATED by scratchpad/gen-zones.mjs from\n platform faceElements.js and are byte-identical to the platform FaceMap.\n PROFILE zones diverge from the platform on purpose: the platform side-profile is a\n photo-less schematic with a stylised, over-deep skull that no rigid fit can register\n onto a real photographed profile. The kit therefore reshapes the platform-authored\n profile paths with a thin-plate-spline warp pinned to anatomical landmarks (crown,\n nasion, eye, nose-tip, subnasale, lips, chin, jaw-angle, ear, back-skull, nape) so the\n mesh traces a real face for the photo overlay. Do not hand-edit the path data: re-run\n the front generator, or the profile TPS bake (scratchpad/faces/tps + bake-zones), and\n paste the output here. Centroids are the path-bounds midpoint, used to anchor finding\n markers. viewBox is the platform 0 0 808 944. */\n\nimport type { FaceLandmarkSeed, FaceZoneSeed } from './face-scheme-data';\n\nexport const FRONT_VIEWBOX = '0 0 808 944';\nexport const FRONT_PIVOT = { x: 404, y: 472 } as const;\n\nexport const FRONT_CONTOUR: FaceZoneSeed = {\n id: 'faceContour',\n labelKey: 'faceContour',\n d: 'M688.38,439.4c-9.78,0-15.37,4.04-17.86,6.47,1.31-10.99,2.5-20.62,3.49-28.2,7.55-57.89,5.03-94.39,5.03-94.39-15.15-165.92-111.73-221.46-186.21-239.39h0s-.1-.02-.1-.02c-6.18-1.49-12.21-2.72-18.02-3.73l-.96-.2c-46.02-9.51-93.5-9.51-139.52,0l-.95.2c-5.82,1.01-11.85,2.24-18.04,3.73l-.08.02h0c-74.48,17.94-171.06,73.48-186.21,239.39,0,0-2.52,36.5,5.03,94.39,1,7.68,2.2,17.45,3.53,28.61-2.25-2.35-7.91-6.88-18.26-6.88-13.97,0-29.06,16.21-20.12,63.71,8.94,47.5,17.3,80.48,18.43,97.24,1.11,16.47,24.08,47.91,41.43,38.23.9,8.33,1.42,13.17,1.42,13.17,0,0,8.81,59.15,56.63,104.46,12.62,11.95,25.84,23.46,38.49,33.94,7.14-4.56,24-16.96,29.06-35.21.9-3.24,1.73-6.33,2.52-9.29,2.46-9.17,4.59-17.1,7.4-24.46,3.44-9.01,7.62-16.28,13.25-23.04-47.54-46.57-49.82-89.3-48.75-104.93-21.75-2.47-38.06-19.4-47.37-49.37-7.27-23.39-7.77-46.39-7.79-47.36-.02-1.34.43-2.57,1.21-3.54-.47-2.21-.65-3.58-.67-3.77-.28-2.23.97-4.38,3.05-5.24.34-.14.69-.23,1.04-.29.47-3.26,2.23-10.86,8.8-18.21,10.82-12.11,28.95-17.92,53.93-17.22,41.5,1.13,55.11,17.49,59.58,28.15,1.45.85,2.49,2.34,2.68,4.14,0,.05.02.21.05.45.22.48.36,1,.42,1.56.04.41.34,3.71.16,8.68.17,6.11.1,14.39-.75,23.68-.27,2.9-.6,5.73-.98,8.49,4.1-9.94,7.68-20.29,11.15-30.32l.31-.89-10.84-54.18c-2.15-3.39-8.76-12.5-21.75-19.51-21.63-11.68-51.16-12.3-85.4-1.8-2.58.79-5.32-.61-6.19-3.16l-53.03-154.45c-.88-1.09-1.31-2.52-1.05-4.01.26-1.43,6.68-35.35,39.46-69.52,19.15-19.96,43.37-35.84,72.01-47.21,35.3-14.02,77.45-21.18,125.28-21.27h0s.98,0,.98,0c47.83.1,89.98,7.25,125.28,21.27,28.63,11.37,52.86,27.26,72.01,47.21,30.62,31.92,38.24,63.61,39.32,68.82.29.8.42,1.35.47,1.59.17.87.11,1.77-.17,2.61l-53.21,154.96c-.88,2.55-3.62,3.95-6.19,3.16-34.24-10.5-63.77-9.88-85.4,1.8-11.42,6.17-17.92,13.96-20.78,18.06l-11.25,56.25.04.11c2.95,8.52,5.98,17.28,9.34,25.81-.14-1.26-.28-2.53-.4-3.82-.53-5.79-.75-11.19-.81-15.92-1.11-9.09-.51-15.85-.45-16.44.11-1.1.59-2.08,1.3-2.85.19-.72.52-1.37.96-1.94,3.86-10.54,16.77-28.3,60.04-29.48,24.93-.69,43.04,5.1,53.85,17.2,6.53,7.3,8.3,14.85,8.78,18.13.53.04,1.05.16,1.56.37,2.08.86,3.32,3.01,3.05,5.24-.02.14-.12.93-.37,2.25,1.7.96,2.84,2.8,2.8,4.89-.02.97-.52,23.97-7.79,47.36-8.99,28.91-24.49,45.69-45.11,49.05,1.16,15.29-.68,58.78-49.42,106.1,5.35,6.56,9.36,13.63,12.68,22.33,2.81,7.36,4.94,15.28,7.4,24.45.79,2.96,1.62,6.05,2.52,9.29,5.07,18.28,21.97,30.69,29.09,35.23,12.65-10.48,25.88-21.99,38.5-33.95,47.82-45.31,56.63-104.46,56.63-104.46,0,0,.51-4.77,1.4-13,17.31,9.1,39.99-22.04,41.09-38.41,1.13-16.77,9.49-49.74,18.43-97.24,8.94-47.5-6.15-63.71-20.12-63.71Z',\n};\n\nexport const FRONT_ZONES: FaceZoneSeed[] = [\n {\n id: 'lateralNeckRight',\n labelKey: 'lateralNeckRight',\n d: 'M234.37,783.6s32.84,133.08,163.93,137.04v-48.41s-103.52,3.15-163.93-88.63Z',\n cx: 316,\n cy: 852,\n side: 'right',\n },\n {\n id: 'lateralNeckLeft',\n labelKey: 'lateralNeckLeft',\n d: 'M572.98,783.6s-32.84,133.08-163.93,137.04v-48.41s103.52,3.15,163.93-88.63Z',\n cx: 491,\n cy: 852,\n side: 'left',\n },\n {\n id: 'forehead',\n labelKey: 'forehead',\n d: 'M404,206.87s-.1,0-.14,0h-1.06s-.09,0-.14,0c-193.61.2-222.63,42.82-226.18,50.14l51.08,148.75c44.34-12.51,72.83-4.7,89.09,4.28,11.9,6.57,19.12,14.63,22.86,19.75,9.12-6.42,21.26-11.05,36.17-13.79,13.2-2.43,24.31-2.56,27.4-2.54.05,0,.1,0,.16,0h.04s.07,0,.11,0h.04c.2,0,.4,0,.64,0,3.65,0,14.33.22,26.92,2.54,14.91,2.74,27.05,7.37,36.17,13.79,3.74-5.12,10.96-13.18,22.86-19.75,16.26-8.98,44.75-16.79,89.09-4.28l51.08-148.75c-3.54-7.32-32.57-49.94-226.18-50.14Z',\n cx: 403,\n cy: 318,\n side: 'midline',\n },\n {\n id: 'glabella',\n labelKey: 'glabella',\n d: 'M403.28,472.75s.03,0,.04,0c.11,0,.22,0,.34,0,.11,0,.22,0,.34,0,.02,0,.03,0,.04,0,28.06.09,43.26,5.62,51.09,10.26l8.65-43.23c-19.58-15.79-54.78-16.31-59.89-16.33-.13,0-.27,0-.4,0h-.16s-.16,0-.16,0c-.13,0-.27,0-.4,0-5.09-.02-39.63.51-59.32,15.85l8.74,43.71c7.83-4.64,23.03-10.17,51.09-10.26Z',\n cx: 404,\n cy: 453,\n side: 'midline',\n },\n {\n id: 'nasalBase',\n labelKey: 'nasalBase',\n d: 'M403.9,608.63c-.19.02-.38.06-.58.06-34.25,0-47.76,9.72-52.65,15.19-.02.21-.05.42-.1.62,10.07,8.84,28.11,13.48,52.75,13.48.15,0,.29.03.43.04.15-.01.29-.04.43-.04,24.98,0,43.18-4.76,53.17-13.83-4.69-5.41-18.09-15.46-52.89-15.46-.2,0-.38-.04-.58-.06Z',\n cx: 404,\n cy: 623,\n side: 'midline',\n },\n {\n id: 'nasolabialFold',\n labelKey: 'nasolabialFold',\n d: 'M347.77,580.98c-2.28,3.78-4.09,6.76-5.04,9.96-1.38,4.58-3.68,15.46,1.36,25.35,7.25-7.51,23.63-17.61,59.24-17.61.2,0,.38.04.58.06.19-.02.38-.06.58-.06,35.31,0,51.71,9.93,59.05,17.42,4.92-9.84,2.63-20.61,1.27-25.17-.96-3.19-2.76-6.18-5.04-9.96-5.23-8.65-12.39-20.5-14.97-45.03-2.34-22.26,2.15-36.18,6.11-43.75-5.65-3.61-19.1-9.4-47.23-9.45-27.9.05-41.36,5.75-47.09,9.36,3.98,7.55,8.51,21.49,6.16,43.84-2.58,24.53-9.74,36.38-14.97,45.03Z',\n cx: 404,\n cy: 550,\n side: 'midline',\n },\n {\n id: 'medialCheekboneRight',\n labelKey: 'medialCheekboneRight',\n d: 'M310.99,528.29c6.75-7.41,10.08-16.87,11.7-25.19-.55.67-1.13,1.34-1.73,2.02-11.93,13.41-27.69,20.63-45.58,20.88-24.57.33-43.87-10.86-55.92-20.57,6.83,15.71,22.29,36.28,56.41,36.28,15.2,0,27.02-4.51,35.12-13.42Z',\n cx: 271,\n cy: 522,\n side: 'right',\n },\n {\n id: 'medialCheekboneLeft',\n labelKey: 'medialCheekboneLeft',\n d: 'M529.65,526c-17.89-.25-33.66-7.47-45.58-20.88-.48-.54-.94-1.08-1.39-1.62.02.49.04.99.06,1.49,1.74,7.84,5.06,16.45,11.3,23.3,8.11,8.9,19.92,13.42,35.12,13.42,34.11,0,49.57-20.57,56.41-36.28-12.05,9.71-31.35,20.9-55.92,20.57Z',\n cx: 534,\n cy: 523,\n side: 'left',\n },\n {\n id: 'lateralCheekLeft',\n labelKey: 'lateralCheekLeft',\n d: 'M540.15,593.37c-18.18-.45-32.31-6.73-43.63-16.5-1.18-.24-2.29-.88-3.12-1.88-1.11-1.34-2.16-2.75-3.18-4.2-15.93-17.11-25.66-41.5-33.74-64.38-1.86,6.64-3.07,15.9-1.75,28.49,2.35,22.31,8.58,32.62,13.58,40.91,2.44,4.04,4.74,7.85,6.06,12.26,1.9,6.35,5.16,22.19-4,36.16,7.6,9.08,23.47,31.53,26.94,63.94,25.78-26.28,36.07-50.77,40.13-67.19,3.11-12.6,3.1-22.36,2.72-27.61Z',\n cx: 497,\n cy: 597,\n side: 'left',\n },\n {\n id: 'lateralCheekRight',\n labelKey: 'lateralCheekRight',\n d: 'M350.8,505.59c-14.12,40.11-33.1,85.3-81.73,87.86-.37,5.26-.37,15,2.72,27.53,4.02,16.29,14.18,40.52,39.5,66.55,3.52-31.28,18.56-53.18,26.31-62.61-9.73-14.13-6.4-30.4-4.46-36.85,1.32-4.41,3.62-8.22,6.06-12.26,5-8.29,11.23-18.6,13.58-40.91,1.38-13.12,0-22.63-1.99-29.32Z',\n cx: 311,\n cy: 597,\n side: 'right',\n },\n {\n id: 'zygomaticArchRight',\n labelKey: 'zygomaticArchRight',\n d: 'M319.21,534.06c-.26.3-.51.61-.78.91-10.08,11.1-24.4,16.73-42.56,16.73-30.94,0-48.47-15.12-58-28.84,4.69,25.35,16.61,59.69,46.82,59.69,16.21,0,28.81-5.46,38.96-14.31.06-.09.11-.18.18-.26,7.83-9.48,12.57-21.69,15.39-33.93Z',\n cx: 269,\n cy: 553,\n side: 'right',\n },\n {\n id: 'zygomaticArchLeft',\n labelKey: 'zygomaticArchLeft',\n d: 'M542.24,582.4c31.74,0,43.29-37.91,47.48-63.47-.28.46-.57.93-.86,1.4-9.09,14.32-26.82,31.38-59.69,31.38-18.08,0-32.36-5.59-42.43-16.6,2.41,10.07,6.15,20.06,11.85,28.48,10.9,11.46,24.85,18.81,43.65,18.81Z',\n cx: 538,\n cy: 551,\n side: 'left',\n },\n {\n id: 'upperForehead',\n labelKey: 'upperForehead',\n d: 'M524.46,147.46c-34.16-13.49-75.08-20.35-121.64-20.4-46.56.05-87.49,6.91-121.64,20.4-27.26,10.76-50.3,25.76-68.48,44.56-15.39,15.91-24.67,31.94-30.13,44.09,4.92-3.6,11.3-7.4,19.61-11.17,40.86-18.52,108.25-27.96,200.32-28.07.05,0,.11,0,.16,0h.68s.68,0,.68,0c.05,0,.11,0,.16,0,92.07.1,159.46,9.55,200.32,28.07,7.48,3.39,13.39,6.81,18.08,10.08-5.52-12-14.69-27.55-29.63-43-18.18-18.8-41.22-33.79-68.48-44.56Z',\n cx: 403,\n cy: 182,\n side: 'midline',\n },\n {\n id: 'marionetteLines',\n labelKey: 'marionetteLines',\n d: 'M344.66,676.82c23.78-8.45,44.18-6.91,59.31,4.4,15.13-11.32,35.54-12.87,59.33-4.41,9.61,3.41,17.99,7.81,23.93,11.32-3.32-27.6-16.56-47.44-23.55-56.2-11.99,10.66-31.94,16.05-59.49,16.05-.15,0-.29-.03-.43-.04-.15.01-.29.04-.43.04-27.09,0-46.83-5.21-58.88-15.52-7.05,8.98-19.76,28.44-23.08,55.31,5.9-3.44,14.03-7.65,23.29-10.94Z',\n cx: 404,\n cy: 660,\n side: 'midline',\n },\n {\n id: 'lowerLip',\n labelKey: 'lowerLip',\n d: 'M487.84,709.63c-4.92,5.03-11.64,11.3-19.65,17.27-17.67,13.18-35.13,19.92-51.28,19.92-4.52,0-8.94-.53-13.23-1.6-4.29,1.06-8.71,1.6-13.23,1.6-16.15,0-33.61-6.74-51.28-19.92-7.36-5.49-13.63-11.23-18.42-16.03,1.76,38.11,15.57,71.15,82.56,71.15.33,0,.64.04.95.1.31-.06.62-.1.95-.1,67.72,0,81.09-33.76,82.62-72.39Z',\n cx: 404,\n cy: 746,\n side: 'midline',\n },\n {\n id: 'lipCommissures',\n labelKey: 'lipCommissures',\n d: 'M407.45,691.27c-.9.8-2.02,1.19-3.13,1.23-1.34.13-2.72-.26-3.8-1.22-22.23-19.67-55.63-5.3-72.53,4.24,11.5,1.38,25.87,2.57,31.61,1.13,6.72-1.68,12.32.35,18.25,2.5,6.08,2.2,13.64,4.94,25.48,4.98,11.85-.04,19.41-2.78,25.48-4.98,5.93-2.15,11.53-4.18,18.25-2.5,5.93,1.48,21.03.17,32.69-1.26-16.96-9.54-50.17-23.72-72.31-4.13Z',\n cx: 404,\n cy: 688,\n side: 'midline',\n },\n {\n id: 'upperLip',\n labelKey: 'upperLip',\n d: 'M444.65,706.36c-3.59-.9-6.73.14-12.42,2.2-6.4,2.32-15.13,5.47-28.52,5.57-.03,0-.06,0-.09,0-.09,0-.18,0-.28,0-.09,0-.18,0-.28,0-.03,0-.06,0-.09,0-13.39-.1-22.12-3.25-28.52-5.57-5.69-2.06-8.83-3.1-12.42-2.2-6.87,1.72-20.85.72-32.07-.53,15.47,14.96,44.16,37.13,72.34,29.42.46-.13.93-.16,1.39-.15.46,0,.93.03,1.4.15,28.22,7.72,56.98-14.55,72.43-29.5-11.35,1.29-25.82,2.37-32.86.61Z',\n cx: 404,\n cy: 724,\n side: 'midline',\n },\n {\n id: 'submental',\n labelKey: 'submental',\n d: 'M402.47,796.81c-31.67.12-55.93,8.97-72.11,26.31-5.47,5.87-9.06,11.79-11.35,16.53.85.63,1.68,1.27,2.5,1.95,4.99,4.16,26.68,19.07,81.31,19.07s76.32-14.91,81.31-19.07c.65-.54,1.31-1.06,1.98-1.57-2.28-4.8-5.91-10.88-11.53-16.91-16.17-17.35-40.44-26.19-72.11-26.31Z',\n cx: 403,\n cy: 829,\n side: 'midline',\n },\n {\n id: 'mandibularAngleLeft',\n labelKey: 'mandibularAngleLeft',\n d: 'M514.03,757.42c-.91-3.27-1.74-6.39-2.54-9.37-2.39-8.91-4.46-16.6-7.08-23.48-1.84-4.83-3.93-9.07-6.37-12.99-1.26,23.6-6.85,40.74-17.31,53.43-10.17,12.33-24.87,20.32-44.53,24.16,17.13,3.95,34.7,11.85,48.39,26.79,5.59,6.1,9.46,12.22,12.09,17.4,10.43-7.53,28.1-20.6,47.98-36.72-9.19-6.29-25.19-19.64-30.61-39.21Z',\n cx: 490,\n cy: 772,\n side: 'left',\n },\n {\n id: 'mandibularAngleRight',\n labelKey: 'mandibularAngleRight',\n d: 'M372.3,789.03c-19.33-3.89-33.81-11.84-43.86-24.02-10.59-12.83-16.18-30.24-17.35-54.26-2.69,4.15-4.95,8.66-6.92,13.84-2.63,6.88-4.69,14.57-7.08,23.48-.8,2.98-1.64,6.1-2.54,9.37-5.42,19.55-21.39,32.89-30.58,39.19,19.56,15.86,36.98,28.78,47.47,36.36,2.63-5.1,6.44-11.07,11.9-17.03,13.85-15.11,31.67-23.02,48.98-26.92Z',\n cx: 318,\n cy: 772,\n side: 'right',\n },\n {\n id: 'eyelidRight',\n labelKey: 'eyelidRight',\n d: 'M270.93,458.23c-50.38-1.36-52.57,26.33-52.64,27.51-.08,1.39-.72,2.61-1.69,3.46,7.87,8.21,29.3,27.23,58.63,26.79,29.72-.41,43.34-22.6,47.54-31.28-.03-.39-.05-.75-.07-1.07-1.34-6.07-8.94-24.24-51.76-25.41Z',\n cx: 270,\n cy: 487,\n side: 'right',\n },\n {\n id: 'eyelidLeft',\n labelKey: 'eyelidLeft',\n d: 'M586.21,485.71c-.08-1.27-2.3-28.82-52.55-27.45-36.35.99-47.27,14.25-50.55,21.95.08.48.1.98.05,1.49-.01.11-.16,1.63-.31,4.22,4.82,9.37,18.52,29.69,46.94,30.08,29.04.43,50.34-18.21,58.4-26.55-1.13-.87-1.91-2.2-1.98-3.73Z',\n cx: 536,\n cy: 487,\n side: 'left',\n },\n];\n\nexport const FRONT_LANDMARKS: FaceLandmarkSeed[] = [\n { id: 'right-eye', labelKey: 'rightEye', x: 268, y: 487 },\n { id: 'left-eye', labelKey: 'leftEye', x: 535, y: 487 },\n { id: 'nose-tip', labelKey: 'noseTip', x: 403, y: 620 },\n { id: 'chin', labelKey: 'chin', x: 404, y: 858 },\n];\n\n/* Side-profile zone paths (left-facing), kept for a future profile view. Not yet\n wired into FACE_VIEWS — front is the photo-mapping surface today. */\nexport const PROFILE_ZONE_PATHS: FaceZoneSeed[] = [\n {\n id: 'cheekbone',\n labelKey: 'cheekbone',\n d: 'M587.86 415.87C586.01 408.8 583.79 409.02 576.91 410.15C573.08 410.38 567.86 410.32 561.24 408.36C547.07 403.39 488.66 358.5 465.97 345.98C436.12 334.42 407.31 328.09 390.38 326.48C395.19 331.07 399.27 338.58 388.69 345.72C397.48 346.49 408.73 348.34 421.39 351.61C467.3 374.36 523.12 420.78 541.55 435.68C550.62 443.21 555.39 449.39 559.11 454.28C564.66 461.73 567.89 466.16 579.26 467.46C583.31 467.67 586.22 465.29 588.19 460.34C591.18 452.28 592.1 431.43 587.86 415.88Z',\n cx: 490,\n cy: 397,\n },\n {\n id: 'profileContour',\n labelKey: 'profileContour',\n d: 'M657.04 427.15C645.32 417.42 612.92 377.67 608.47 342.76C610.05 315.91 614.77 299.37 606.91 241.79C600.19 175.54 556.62 116.74 420.63 130.08C253.44 160.79 152.79 273.93 132.98 380.57C116.36 516.03 182.43 589.12 182.43 589.12L226.57 641.41C234.86 650.15 249.78 644.43 261.09 627.35L321.54 525.13C328.57 512.58 325.7 495.54 318.17 493.61C294.63 488.83 257.86 468.83 262.84 409.94C267.69 380.06 284 357.04 302.38 340.78C330.2 319.79 367.62 311.97 382.74 320.74C382.87 320.84 382.94 320.94 382.99 321.04C388.82 321.28 405.44 322.73 426.75 326.43C424.09 323.46 422.53 319.98 422.13 315.98C421.41 309.9 423.49 304.51 423.88 303.54C426.23 284.75 477.87 242.7 506.61 211.31L506.84 210.99C508.32 208.89 511.62 207.65 515.45 207.77C519.23 207.92 522.88 209.42 524.98 211.72L567.11 326.31C568.13 330.65 567.2 335.05 564.76 336.69C563.35 337.54 561.24 339.5 558.91 342.14C559.06 342.41 559.15 342.57 559.15 342.57L573.97 372.38C575.71 369.67 578.22 365.84 581.33 362.37C585.49 356.14 590.79 348.98 597.61 352.24C598.15 352.43 598.76 354.7 598.84 357.08C598.91 358.84 598.59 359.88 598.14 359.69C597.37 359.38 596.65 359.2 595.96 359.12C597.33 369.53 600.96 398.18 595.38 398C588.92 397.38 587.72 374.56 588.46 362.31C586.69 363.79 584.85 365.85 582.96 368.5C580.04 371.85 577.73 375.59 576.26 377.97L577.27 380.55C577.56 381.29 577.66 382.22 577.55 383.11C577.43 384 577.11 384.76 576.66 385.24L553.04 396.96C558.13 400.5 560.86 401.88 562.35 402.34C568.32 404.04 572.97 404.02 576.7 403.74C583.09 402.53 587.8 400.9 590.94 412.75C594.16 424.28 596.78 449.29 591.87 464.27C589.18 471.56 584.78 475.46 579.05 475.22C575.99 474.97 573.36 474.4 571.04 473.57C567.75 472.35 565.15 470.69 562.9 468.71C562.88 472.1 562.76 476.4 562.38 481.46C567.8 483.15 578.56 487.63 584.78 500.94C588.58 509.77 589.68 521.45 587.53 535.74C590.03 539.27 597.49 548.85 608.32 547.11C615.16 544.28 621.37 543.88 625.6 545.18C627.13 536.84 628.35 525.82 627.96 516.71C626.08 501.81 623.74 494.92 625.48 485.82C627.82 477.03 640.49 466.24 651.72 451.57C662.37 438.81 663.39 438.52 657.07 427.15Z',\n cx: 390,\n cy: 383,\n },\n {\n id: 'temple',\n labelKey: 'temple',\n d: 'M551.29 339.27C551.73 338.72 552.17 338.34 552.62 338.11C556.19 334.28 559.65 331.21 562.14 329.85C562.68 329.54 562.9 328.65 562.67 327.75L518.07 213.95C517.55 213.37 516.78 213.26 516.36 213.24C515.95 213.22 515.19 213.27 514.81 213.81L514.57 214.13C507.63 223.01 490.31 241.35 473.08 259.55C456.86 274.15 435.38 293.06 434.06 303.22L434.04 303.36L433.97 303.51C433.88 303.72 424.42 324.26 448.09 332.54C448.37 332.66 448.62 332.79 448.84 332.93C456.22 335.74 463.82 338.97 471.46 342.54C502.66 360.75 523.25 375.04 536.66 384.85C535.73 382.31 534.95 379.47 534.57 376.45C533.66 370.15 534.7 364.78 536.86 360.11C539.61 349.89 545.41 345.33 549.41 341.41C549.46 341.36 549.51 341.3 549.56 341.25C550.19 340.57 550.77 339.92 551.29 339.26Z',\n cx: 494,\n cy: 299,\n },\n {\n id: 'preauricularArea',\n labelKey: 'preauricularArea',\n d: 'M399.04 423.47C400.19 426.46 399.28 429.83 397.03 431.07L421.24 493.26C429.33 496.6 440.56 504.27 451.47 519.28C455.98 526.45 460.47 535.45 464.49 546.37C473.34 527.06 479.06 492.31 479.5 447.01C480.52 417.95 479.02 394.87 478.75 390.28C457.75 375.55 435.02 362.96 415.32 355.59C403.76 352.86 393.4 351.38 385.05 350.82C383.53 355.04 382.54 362.85 384.93 378.19C389.1 396.84 398.92 423.16 399.04 423.46Z',\n cx: 432,\n cy: 449,\n },\n {\n id: 'mandibularBody',\n labelKey: 'mandibularBody',\n d: 'M448.9 529.33C441.36 518.39 433.39 511.79 426.69 508.14L444.05 565.22L444.11 565.55C444.61 568.53 446.18 612.75 465.58 621.58C467.31 622.11 469.2 622.59 471.21 623.04C472.27 587.41 463.32 555.13 448.9 529.33Z',\n cx: 449,\n cy: 566,\n },\n {\n id: 'submentalProfile',\n labelKey: 'submentalProfile',\n d: 'M499.51 624.48C500.22 624.28 501.01 624.05 501.85 623.78C506.95 622.1 508.34 617.25 499.33 608.72C499.3 608.71 499.28 608.69 499.25 608.67C498.87 608.43 498.48 608.2 498.08 607.95C489.61 603.09 481.6 597.71 477.16 594.66C477.83 603.57 477.42 613.28 475.61 623.94C494.7 628.41 483.66 636.08 483.66 636.08C483.66 636.08 476.69 649.28 527.7 627.3C518.99 627.63 510.52 627.34 503.56 626.55C496.4 626.16 495.89 625.71 499.5 624.48Z',\n cx: 502,\n cy: 622,\n },\n {\n id: 'masseter',\n labelKey: 'masseter',\n d: 'M554.71 459.19C550.92 454.22 546.49 448.51 537.91 441.39C528.42 433.63 510.52 417.13 488.4 397.93C489.3 421.03 491.75 514.66 467.63 555.34C470.6 563.87 473.34 573.19 475.46 583.39C476.18 582.97 476.93 582.94 477.54 583.4C477.77 583.56 487.55 590.84 499.76 598.04C530.58 563.84 546.09 529.73 552.28 505.65C557.13 483.51 556.81 466.48 556.58 461.68C555.96 460.87 555.34 460.04 554.7 459.2Z',\n cx: 512,\n cy: 498,\n },\n {\n id: 'cheek',\n labelKey: 'cheek',\n d: 'M503.7 600.29L503.69 600.76C506.68 599.48 519.15 594.49 532.76 586.74C551.63 579.2 568.84 567.08 579.23 546.08C584.39 529.37 584.46 515.84 580.48 506.25C575.46 495.21 566.3 491.13 561.55 489.57C560.86 495.06 559.83 501.15 558.34 507.68C551.78 532.36 535.25 566.52 503.69 600.3Z',\n cx: 544,\n cy: 545,\n },\n {\n id: 'chin',\n labelKey: 'chin',\n d: 'M622.37 557.72C618.53 556.38 613.08 555.94 606.76 557.59C595.83 558.3 588.65 550.46 584.99 545.44C584.69 546.34 584.38 547.24 584.03 548.15C571.22 572.11 550.01 585.32 530.6 593C522.53 597.65 514.89 601.46 509.24 604.26C515.21 610.11 514.33 615.81 510.02 618.18C502.34 623.58 523.79 624.61 543.95 623.09C547.34 622.17 550.83 621.19 554.45 620.12L594.99 602.7L595.21 602.54C596.13 601.8 602.57 595.37 608.29 593.49C613.07 590.42 621.01 568.35 622.15 559.26C622.19 558.76 622.27 558.26 622.36 557.75Z',\n cx: 562,\n cy: 585,\n },\n {\n id: 'browTail',\n labelKey: 'browTail',\n d: 'M555.64 346.57C555.64 346.57 530.25 360.36 547.29 391.38L572.95 381.28L555.64 346.57Z',\n cx: 552,\n cy: 369,\n },\n];\n","/* ------------------------------------------------------------------ */\n/* FaceScheme — data module (pure, no React). */\n/* */\n/* The clinical face-zone taxonomy (aesthetic-medicine front view), */\n/* the finding vocabulary + its shared marker glyphs, and the affine */\n/* registration maths used to map the canonical zone mesh onto a real */\n/* patient photo. This module is the swap seam: the FRONT zone `d` paths */\n/* are byte-identical to the platform FaceMap; the PROFILE paths are */\n/* TPS-reshaped to trace a real face (see face-scheme-zones.ts). */\n/* */\n/* Registration is a 6-DOF affine — independent horizontal + vertical */\n/* scale, rotation, shear and translation — emitted as an SVG */\n/* `transform` matrix on the mesh group (never an inline style). The */\n/* same `solveAffine` least-squares fit serves manual landmark drag */\n/* AND a consumer-/kit-run face detector (detect-face-landmarks.ts). */\n/* ------------------------------------------------------------------ */\n\nimport type { MarkerShape, MarkerTone } from '../annotation-marker';\nimport {\n FRONT_CONTOUR,\n FRONT_LANDMARKS,\n FRONT_PIVOT,\n FRONT_VIEWBOX,\n FRONT_ZONES,\n PROFILE_ZONE_PATHS,\n} from './face-scheme-zones';\n\n/* ------------------------------------------------------------------ */\n/* Geometry vocabulary */\n/* ------------------------------------------------------------------ */\n\nexport type FaceViewId = 'front' | 'profile-left' | 'profile-right';\nexport type FaceSide = 'left' | 'right' | 'midline';\n\nexport interface Point {\n x: number;\n y: number;\n}\n\n/** Raw zone shape emitted by the generator (cx/cy/side present for clickable\n * zones, absent on the contour guide). */\nexport interface FaceZoneSeed {\n id: string;\n labelKey: string;\n d: string;\n cx?: number;\n cy?: number;\n side?: FaceSide;\n}\nexport interface FaceLandmarkSeed {\n id: string;\n labelKey: string;\n x: number;\n y: number;\n}\n\n/** A clickable face zone — a region of the canonical mesh. */\nexport interface FaceZone {\n id: string;\n /** `faceScheme.zone.<labelKey>` translation key. */\n labelKey: string;\n /** SVG path data in the view's coordinate space. */\n d: string;\n /** Path-bounds centroid — anchors the finding marker. */\n cx: number;\n cy: number;\n /** Patient-anatomical side (not reading direction). */\n side: FaceSide;\n}\n\n/** A canonical landmark anchor used to fit the mesh to a photo. */\nexport interface FaceLandmark {\n id: string;\n /** `faceScheme.landmark.<labelKey>` translation key. */\n labelKey: string;\n x: number;\n y: number;\n}\n\nexport interface FaceView {\n id: FaceViewId;\n /** `faceScheme.view.<labelKey>` translation key. */\n labelKey: string;\n viewBox: string;\n /** Pivot for the registration controls (the face centre). */\n pivot: Point;\n /** The face outline, drawn as a non-interactive guide. */\n contour: { id: string; labelKey: string; d: string };\n zones: FaceZone[];\n /** Canonical anchors for landmark fitting / auto-detect. */\n landmarks: FaceLandmark[];\n /** Render the mesh horizontally mirrored (right profile = mirrored left). */\n mirror?: boolean;\n}\n\n// The side-profile contour is one of the profile seeds; the rest are the\n// clickable profile zones. They carry no `side` (a profile is single-sided),\n// so default it to 'midline' for the data-side hook.\nconst PROFILE_CONTOUR =\n PROFILE_ZONE_PATHS.find((z) => z.id === 'profileContour') ?? FRONT_CONTOUR;\nconst PROFILE_ZONES: FaceZone[] = PROFILE_ZONE_PATHS.filter(\n (z) => z.id !== 'profileContour',\n).map((z) => ({\n id: z.id,\n labelKey: z.labelKey,\n d: z.d,\n cx: z.cx ?? 0,\n cy: z.cy ?? 0,\n side: z.side ?? 'midline',\n}));\n\n// Canonical profile anchors on the reshaped side-profile mesh (the face faces +x).\n// A non-collinear EYE / NOSE-TIP / MOUTH triangle — exactly the visible-side\n// keypoints the YuNet profile detector returns, which (unlike the old collinear\n// nose-tip/nasion/subnasale/chin midline points) give the affine solver a\n// well-conditioned, shear-free fit. Because the profile mesh is TPS-reshaped to\n// sit on the canonical demo face, these coordinates are the detector's keypoints\n// *on that face* — so Auto-detect resolves to ~identity and preserves the fit\n// rather than re-distorting it. They double as the manual drag handles; the right\n// profile reuses them and mirrors the rendered result. See detect-face-yunet.ts.\nconst PROFILE_LANDMARKS: FaceLandmark[] = [\n { id: 'eye', labelKey: 'eye', x: 561, y: 374 },\n { id: 'nose-tip', labelKey: 'noseTip', x: 668, y: 432 },\n { id: 'mouth', labelKey: 'mouth', x: 592, y: 532 },\n];\n\nexport const FACE_VIEWS: Record<FaceViewId, FaceView> = {\n front: {\n id: 'front',\n labelKey: 'front',\n viewBox: FRONT_VIEWBOX,\n pivot: FRONT_PIVOT,\n contour: FRONT_CONTOUR,\n // The generator guarantees every clickable zone carries cx/cy/side.\n zones: FRONT_ZONES as FaceZone[],\n landmarks: FRONT_LANDMARKS,\n },\n // The reshaped profile zones face +x; the right profile is the same mesh\n // mirrored. Both fit on the eye/nose-tip/mouth anchors — auto-detect and manual\n // drag/sliders all work; the right profile mirrors the fit at render time.\n 'profile-left': {\n id: 'profile-left',\n labelKey: 'profileLeft',\n viewBox: FRONT_VIEWBOX,\n pivot: FRONT_PIVOT,\n contour: PROFILE_CONTOUR,\n zones: PROFILE_ZONES,\n landmarks: PROFILE_LANDMARKS,\n },\n 'profile-right': {\n id: 'profile-right',\n labelKey: 'profileRight',\n viewBox: FRONT_VIEWBOX,\n pivot: FRONT_PIVOT,\n contour: PROFILE_CONTOUR,\n zones: PROFILE_ZONES,\n landmarks: PROFILE_LANDMARKS,\n mirror: true,\n },\n};\n\nexport const FACE_VIEW_IDS: FaceViewId[] = [\n 'front',\n 'profile-left',\n 'profile-right',\n];\n\n/* ------------------------------------------------------------------ */\n/* Finding vocabulary — generic clinical annotations. */\n/* */\n/* Deliberately practice-neutral: the consumer maps these to its own */\n/* clinical meaning. The SHAPE is the accessible channel (WCAG 1.4.1); */\n/* tone is decorative reinforcement only. */\n/* ------------------------------------------------------------------ */\n\nexport type FaceFindingType = 'observation' | 'treatment' | 'concern' | 'note';\n\nexport const FACE_FINDING_TYPES: FaceFindingType[] = [\n 'observation',\n 'treatment',\n 'concern',\n 'note',\n];\n\nexport const FACE_FINDING_MARKER: Record<\n FaceFindingType,\n { shape: MarkerShape; tone: MarkerTone }\n> = {\n observation: { shape: 'circle', tone: 'info' },\n treatment: { shape: 'square', tone: 'success' },\n concern: { shape: 'triangle', tone: 'warning' },\n note: { shape: 'diamond', tone: 'neutral' },\n};\n\nexport interface FaceFinding {\n type: FaceFindingType;\n /** Optional free-text note (PHI — never surfaced in the agent snapshot). */\n note?: string;\n}\n\n/** Findings keyed by zone id. */\nexport type FaceChart = Record<string, FaceFinding[]>;\n\n/* ------------------------------------------------------------------ */\n/* Registration — 6-DOF affine maths (pure). */\n/* */\n/* A `FaceAffine` is the SVG matrix(a b c d e f): x' = a·x + c·y + e, */\n/* y' = b·x + d·y + f. Applied as a `transform` ATTRIBUTE on the mesh */\n/* group, so it never becomes an inline style. */\n/* ------------------------------------------------------------------ */\n\nexport type FaceAffine = readonly [\n number,\n number,\n number,\n number,\n number,\n number,\n];\n\nexport const IDENTITY_AFFINE: FaceAffine = [1, 0, 0, 1, 0, 0];\n\n/** Per-view registration matrices. */\nexport type FaceRegistration = Partial<Record<FaceViewId, FaceAffine>>;\n\nexport function affineToString(m: FaceAffine): string {\n return `matrix(${m[0]} ${m[1]} ${m[2]} ${m[3]} ${m[4]} ${m[5]})`;\n}\n\nexport function applyAffine(m: FaceAffine, p: Point): Point {\n return {\n x: m[0] * p.x + m[2] * p.y + m[4],\n y: m[1] * p.x + m[3] * p.y + m[5],\n };\n}\n\n/** Editable decomposition of an affine about a pivot — what the numeric\n * registration panel exposes. Angles are degrees. */\nexport interface AffineParts {\n scaleX: number;\n scaleY: number;\n /** degrees */\n rotation: number;\n /** degrees */\n shear: number;\n translateX: number;\n translateY: number;\n}\n\nconst DEG = Math.PI / 180;\n\nexport function composeAffine(parts: AffineParts, pivot: Point): FaceAffine {\n const rad = parts.rotation * DEG;\n const ca = Math.cos(rad);\n const sa = Math.sin(rad);\n const k = Math.tan(parts.shear * DEG);\n const a = parts.scaleX * ca;\n const b = parts.scaleX * sa;\n const c = parts.scaleY * (k * ca - sa);\n const d = parts.scaleY * (k * sa + ca);\n const e = parts.translateX + pivot.x - (a * pivot.x + c * pivot.y);\n const f = parts.translateY + pivot.y - (b * pivot.x + d * pivot.y);\n return [a, b, c, d, e, f];\n}\n\nexport function decomposeAffine(m: FaceAffine, pivot: Point): AffineParts {\n const [a, b, c, d, e, f] = m;\n const scaleX = Math.hypot(a, b);\n const rotation = Math.atan2(b, a) / DEG;\n const det = a * d - b * c;\n const scaleY = scaleX === 0 ? 0 : det / scaleX;\n const shear = Math.atan2(a * c + b * d, det) / DEG;\n const translateX = e - pivot.x + (a * pivot.x + c * pivot.y);\n const translateY = f - pivot.y + (b * pivot.x + d * pivot.y);\n return { scaleX, scaleY, rotation, shear, translateX, translateY };\n}\n\n/**\n * Least-squares 6-DOF affine mapping `src` → `dst` (≥3 non-collinear pairs).\n * Independent horizontal + vertical scale fall out of the fit — this is the\n * shared solver behind both manual landmark drag and face-detector auto-fit.\n * Returns the identity if the system is degenerate.\n */\nexport function solveAffine(src: Point[], dst: Point[]): FaceAffine {\n const n = Math.min(src.length, dst.length);\n if (n < 3) return IDENTITY_AFFINE;\n let Sxx = 0;\n let Sxy = 0;\n let Sx = 0;\n let Syy = 0;\n let Sy = 0;\n let bx0 = 0;\n let bx1 = 0;\n let bx2 = 0;\n let by0 = 0;\n let by1 = 0;\n let by2 = 0;\n for (let i = 0; i < n; i++) {\n const s = src[i];\n const t = dst[i];\n if (!s || !t) continue;\n Sxx += s.x * s.x;\n Sxy += s.x * s.y;\n Sx += s.x;\n Syy += s.y * s.y;\n Sy += s.y;\n bx0 += t.x * s.x;\n bx1 += t.x * s.y;\n bx2 += t.x;\n by0 += t.y * s.x;\n by1 += t.y * s.y;\n by2 += t.y;\n }\n // 3×3 normal matrix N (symmetric); solve N·[a c e]ᵀ = bx and N·[b d f]ᵀ = by.\n const det =\n Sxx * (Syy * n - Sy * Sy) -\n Sxy * (Sxy * n - Sy * Sx) +\n Sx * (Sxy * Sy - Syy * Sx);\n if (Math.abs(det) < 1e-9) return IDENTITY_AFFINE;\n // Inverse of the symmetric 3×3, reused for both right-hand sides.\n const i00 = Syy * n - Sy * Sy;\n const i01 = Sx * Sy - Sxy * n;\n const i02 = Sxy * Sy - Sx * Syy;\n const i11 = Sxx * n - Sx * Sx;\n const i12 = Sx * Sxy - Sxx * Sy;\n const i22 = Sxx * Syy - Sxy * Sxy;\n const solve = (\n r0: number,\n r1: number,\n r2: number,\n ): [number, number, number] => [\n (i00 * r0 + i01 * r1 + i02 * r2) / det,\n (i01 * r0 + i11 * r1 + i12 * r2) / det,\n (i02 * r0 + i12 * r1 + i22 * r2) / det,\n ];\n const [a, c, e] = solve(bx0, bx1, bx2);\n const [b, d, f] = solve(by0, by1, by2);\n return [a, b, c, d, e, f];\n}\n","/* ------------------------------------------------------------------ */\n/* FaceScheme — patient-photo face-zone mapping (aesthetic medicine). */\n/* */\n/* A consumer-supplied patient photo renders as the <image> base; the */\n/* clinical face-zone mesh (byte-identical to the platform FaceMap) */\n/* floats on top as transparent CLICK HOTSPOTS with a crisp 1px outline. */\n/* Because the mesh is a canonical schematic and the photo is an */\n/* arbitrary face, the mesh is REGISTERED onto the photo with a 6-DOF */\n/* affine — independent horizontal/vertical scale, rotation, shear and */\n/* offset — driven by draggable landmark points, accessible numeric */\n/* sliders, or the optional in-kit face detector (lazy-loaded). Findings */\n/* are marked with the shared AnnotationMarker glyph (SHAPE carries the */\n/* meaning, never colour alone) and ride the registration transform. */\n/* */\n/* PHI: the kit is render-only. The photo arrives as a prop and is only */\n/* ever drawn into <image href>; it is never fetched, persisted, logged, */\n/* or exposed through the agent snapshot. The detector runs on-device. */\n/* ------------------------------------------------------------------ */\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type ChangeEvent,\n type KeyboardEvent,\n type PointerEvent as ReactPointerEvent,\n} from 'react';\nimport * as RadixTooltip from '@radix-ui/react-tooltip';\nimport { cva } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { useAgentRegistration } from '../../agent/registry';\nimport {\n AnnotationMarker,\n markerPath,\n type MarkerTone,\n} from '../annotation-marker';\nimport { Button } from '../button';\nimport { Slider } from '../slider';\nimport { RadioGroup, Radio } from '../radio-group';\nimport { faceSchemeAgent } from './face-scheme.agent';\nimport { detectFaceLandmarks } from './detect-face-landmarks';\nimport { detectFaceYuNet } from './detect-face-yunet';\nimport {\n FACE_VIEWS,\n FACE_VIEW_IDS,\n FACE_FINDING_MARKER,\n FACE_FINDING_TYPES,\n IDENTITY_AFFINE,\n affineToString,\n applyAffine,\n composeAffine,\n decomposeAffine,\n solveAffine,\n type FaceAffine,\n type FaceChart,\n type FaceFindingType,\n type FaceRegistration,\n type FaceViewId,\n type FaceZone,\n type Point,\n} from './face-scheme-data';\n\n/* ------------------------------------------------------------------ */\n/* Public types */\n/* ------------------------------------------------------------------ */\n\nexport type FaceDetectStatus =\n | 'idle'\n | 'loading'\n | 'detecting'\n | 'done'\n | 'no-face'\n | 'error';\n\nexport interface FaceDetectOptions {\n /** FRONT view (MediaPipe): base URL for the wasm fileset (defaults to the jsDelivr CDN). */\n wasmBaseUrl?: string;\n /** FRONT view (MediaPipe): URL of the `face_landmarker.task` model (defaults to Google's CDN). */\n modelAssetPath?: string;\n /** PROFILE views (YuNet): ESM bundle URL for `onnxruntime-web` (defaults to the jsDelivr CDN). */\n ortModuleUrl?: string;\n /** PROFILE views (YuNet): base URL for the ORT wasm fileset (defaults to the jsDelivr CDN). */\n ortWasmBaseUrl?: string;\n /** PROFILE views (YuNet): URL of the YuNet `.onnx` model (defaults to the kit's bundled copy). */\n yunetModelUrl?: string;\n}\n\n/**\n * Reduce YuNet's five keypoints to the profile fit triangle. On a 90° profile\n * the contralateral eye / mouth-corner are hallucinated FORWARD (toward the\n * nose), so the VISIBLE ones are the keypoints farther from the nose tip in x.\n * The resulting eye / nose-tip / mouth ids match the profile `PROFILE_LANDMARKS`.\n */\nfunction profileTriangleFromYuNet(\n pairs: Array<{ id: string; photo: Point }>,\n): Array<{ id: string; photo: Point }> | null {\n const photoOf = (id: string) => pairs.find((p) => p.id === id)?.photo;\n const nose = photoOf('nose-tip');\n const rightEye = photoOf('right-eye');\n const leftEye = photoOf('left-eye');\n const rightMouth = photoOf('mouth-right');\n const leftMouth = photoOf('mouth-left');\n if (!nose || !rightEye || !leftEye || !rightMouth || !leftMouth) return null;\n const farther = (a: Point, b: Point) =>\n Math.abs(a.x - nose.x) >= Math.abs(b.x - nose.x) ? a : b;\n return [\n { id: 'eye', photo: farther(rightEye, leftEye) },\n { id: 'nose-tip', photo: nose },\n { id: 'mouth', photo: farther(rightMouth, leftMouth) },\n ];\n}\n\nexport interface FaceSchemeProps {\n /** Opaque instance id — emitted as `data-component-id` for the agent registry. */\n id?: string;\n /**\n * Consumer-supplied patient photo for the FRONT view (shorthand for\n * `viewPhotos.front`). RASTER ONLY (object-URL / `data:image` / app CDN\n * raster) — never an `.svg`. PHI: the kit renders it into an SVG\n * `<image href>` and does nothing else (no fetch, persist, log, transmit) and\n * never exposes `src` via the agent snapshot.\n */\n patientPhoto?: { src: string; altKey?: string };\n /** Per-view photos — each view shows its own image, swapped by the switcher. */\n viewPhotos?: Partial<Record<FaceViewId, { src: string; altKey?: string }>>;\n /** Controlled active view (`front` / `profile-left` / `profile-right`). */\n view?: FaceViewId;\n /** Uncontrolled initial view (default `'front'`). */\n defaultView?: FaceViewId;\n /** Fired when the view switcher changes the active view. */\n onViewChange?: (view: FaceViewId) => void;\n /** Show the built-in radio view switcher (default `true`). */\n showViewSwitcher?: boolean;\n /** `'interactive'` enables zone selection, finding edits and registration; `'display'` is read-only. */\n mode?: 'interactive' | 'display';\n /** Controlled findings — `Record<zoneId, FaceFinding[]>`. */\n value?: FaceChart;\n /** Uncontrolled initial findings. */\n defaultValue?: FaceChart;\n /** Fired whenever the chart changes. */\n onChange?: (next: FaceChart) => void;\n /** Fired when a zone is activated (click / Enter / Space). */\n onSelectZone?: (zoneId: string) => void;\n /** The finding type a zone-click applies in interactive mode (toggles it). */\n activeFinding?: FaceFindingType;\n /** Controlled per-view registration affine. */\n registration?: FaceRegistration;\n /** Uncontrolled initial registration. */\n defaultRegistration?: FaceRegistration;\n /** Fired whenever the registration changes. */\n onRegistrationChange?: (next: FaceRegistration) => void;\n /** `'off'` hides the fit controls; `'adjust'` shows landmark handles + numeric sliders + auto-fit. */\n registrationMode?: 'off' | 'adjust';\n /**\n * Enable the in-kit, on-device face detector for Auto-fit. The front view\n * uses MediaPipe Face Landmarker; the two profile views use YuNet (which,\n * unlike the near-frontal FaceMesh, locates a 90° profile). Both runtimes and\n * their models are lazy-loaded only when detection actually runs, so nothing\n * weighs on the base bundle. Pass options to point at self-hosted models.\n */\n autoDetect?: boolean | FaceDetectOptions;\n /** Observe the detector lifecycle. */\n onDetectStatus?: (status: FaceDetectStatus) => void;\n /**\n * Opt-in: show an on-device photo-capture affordance — a live camera\n * viewfinder (with a file-upload fallback). The captured frame is rendered\n * into the same `<image>` base and emitted via `onCapture`; it is never\n * uploaded, persisted, or logged by the kit. The camera stream is torn down\n * the instant a frame is frozen and on cancel / unmount. Independent of\n * `mode` — set it wherever the practitioner should be able to (re)take the\n * photo.\n */\n allowCapture?: boolean;\n /** Camera to request for capture (default `'user'` — a face self-portrait). */\n captureFacingMode?: 'user' | 'environment';\n /**\n * Fired when a photo is captured or chosen — a `data:` URL, its intrinsic\n * pixel size, and the view it belongs to. PHI: the consumer owns persistence;\n * the kit only renders it.\n */\n onCapture?: (photo: {\n src: string;\n width: number;\n height: number;\n view: FaceViewId;\n }) => void;\n /** Show the finding-type legend below the figure. */\n legend?: boolean;\n /** Accessible label override for the chart region. */\n ariaLabel?: string;\n /** Extra class names on the wrapper. */\n className?: string;\n}\n\nexport interface FaceSchemeHandle {\n /** Focus a zone by id. */\n focusZone: (zoneId: string) => void;\n /** Read the current chart state. */\n getChart: () => FaceChart;\n /** Read the findings on one zone (empty array if none). */\n getZone: (zoneId: string) => FaceChart[string];\n /** Read the zone ids carrying a given finding type. */\n zonesWith: (type: FaceFindingType) => string[];\n /** Add a finding to a zone (no-op if already present). */\n setFinding: (zoneId: string, type: FaceFindingType, note?: string) => void;\n /** Remove a finding type from a zone. */\n removeFinding: (zoneId: string, type: FaceFindingType) => void;\n /** Clear every finding from a zone. */\n clearZone: (zoneId: string) => void;\n /** Read the active-view registration affine. */\n getRegistration: () => FaceAffine;\n /** Set the active-view registration affine. */\n setRegistration: (m: FaceAffine) => void;\n /** Reset the active-view registration to the identity. */\n resetRegistration: () => void;\n /** Solve + apply an affine from canonical-anchor → photo-point pairs. */\n fitToLandmarks: (pairs: { id: string; photo: Point }[]) => void;\n /** Run the on-device detector on the current photo and auto-fit. Resolves\n * `true` on a successful fit, `false` if detection is disabled / finds no face. */\n detect: () => Promise<boolean>;\n /** Start the on-device camera viewfinder (no-op if capture is disabled or the\n * camera API is unavailable — emits an error status in that case). */\n startCapture: () => void;\n /** Stop the camera viewfinder without capturing. */\n cancelCapture: () => void;\n /** Read the active view id (`front` / `profile-left` / `profile-right`). */\n getView: () => FaceViewId;\n /** Switch the active view. */\n setView: (view: FaceViewId) => void;\n}\n\n/* ------------------------------------------------------------------ */\n/* Capture (on-device camera) — privacy-first, mirrors DocumentScanner */\n/* ------------------------------------------------------------------ */\n\nexport type FaceCaptureState = 'idle' | 'requesting' | 'streaming' | 'error';\nexport type FaceCaptureError =\n | 'permission-denied'\n | 'no-camera'\n | 'not-readable'\n | 'insecure-context'\n | 'capture-failed';\n\n/** True when getUserMedia is reachable (secure context + API present). */\nfunction mediaDevicesAvailable(): boolean {\n return (\n typeof navigator !== 'undefined' &&\n typeof navigator.mediaDevices !== 'undefined' &&\n typeof navigator.mediaDevices.getUserMedia === 'function'\n );\n}\n\n/** Map a getUserMedia rejection (a DOMException) to our error kind. */\nfunction classifyMediaError(err: unknown): FaceCaptureError {\n const name = err instanceof Error ? err.name : '';\n if (name === 'NotAllowedError' || name === 'SecurityError')\n return 'permission-denied';\n if (name === 'NotFoundError' || name === 'OverconstrainedError')\n return 'no-camera';\n if (name === 'NotReadableError' || name === 'AbortError')\n return 'not-readable';\n return 'capture-failed';\n}\n\nconst CAPTURE_ERROR_KEY: Record<FaceCaptureError, string> = {\n 'permission-denied': 'faceScheme.capture.error.permissionDenied',\n 'no-camera': 'faceScheme.capture.error.noCamera',\n 'not-readable': 'faceScheme.capture.error.notReadable',\n 'insecure-context': 'faceScheme.capture.error.insecureContext',\n 'capture-failed': 'faceScheme.capture.error.captureFailed',\n};\n\n/* ------------------------------------------------------------------ */\n/* Tone → fill class (no inline styles; tokens only) */\n/* ------------------------------------------------------------------ */\n\nconst markerToneFill: Record<MarkerTone, string> = {\n neutral: 'ds:[fill:var(--muted-foreground)]',\n info: 'ds:[fill:var(--info)]',\n success: 'ds:[fill:var(--success)]',\n warning: 'ds:[fill:var(--warning)]',\n error: 'ds:[fill:var(--destructive)]',\n accent: 'ds:[fill:var(--accent)]',\n};\n\n// One fill declaration per zone resolved through compoundVariants. The outline is\n// a constant 1px (non-scaling-stroke) so it stays crisp under the registration\n// scale; the fill only reveals on hover (interactive) or when the zone is marked.\nconst zoneVariants = cva(\n [\n 'ds:[vector-effect:non-scaling-stroke]',\n 'ds:transition-[fill-opacity] ds:[transition-duration:var(--animation-duration)]',\n 'ds:[fill:transparent] ds:[stroke:var(--accent)] ds:[stroke-width:1]',\n ].join(' '),\n {\n variants: {\n interactive: { true: 'ds:cursor-pointer', false: '' },\n marked: { true: '', false: '' },\n },\n compoundVariants: [\n {\n marked: false,\n interactive: true,\n class: 'ds:hover:[fill:var(--accent)] ds:hover:[fill-opacity:0.14]',\n },\n {\n marked: true,\n class: 'ds:[fill:var(--accent)] ds:[fill-opacity:0.18]',\n },\n ],\n defaultVariants: { interactive: false, marked: false },\n },\n);\n\n/* ------------------------------------------------------------------ */\n/* Helpers */\n/* ------------------------------------------------------------------ */\n\nconst SLIDER_RANGES = {\n scaleX: { min: 0.3, max: 1.8, step: 0.01 },\n scaleY: { min: 0.3, max: 1.8, step: 0.01 },\n rotation: { min: -20, max: 20, step: 0.5 },\n shear: { min: -20, max: 20, step: 0.5 },\n translateX: { min: -240, max: 240, step: 1 },\n translateY: { min: -240, max: 240, step: 1 },\n} as const;\n\ntype SliderField = keyof typeof SLIDER_RANGES;\n// Order mirrors the 6 DOF the pointer-drag fit can reach, so the keyboard path\n// has full parity with the landmark handles (size → angle/skew → position).\nconst SLIDER_FIELDS: SliderField[] = [\n 'scaleX',\n 'scaleY',\n 'rotation',\n 'shear',\n 'translateX',\n 'translateY',\n];\n\n/* ------------------------------------------------------------------ */\n/* FaceScheme */\n/* ------------------------------------------------------------------ */\n\nexport const FaceScheme = forwardRef<FaceSchemeHandle, FaceSchemeProps>(\n (\n {\n id,\n patientPhoto,\n viewPhotos,\n view: viewProp,\n defaultView = 'front',\n onViewChange,\n showViewSwitcher = true,\n mode = 'interactive',\n value,\n defaultValue,\n onChange,\n onSelectZone,\n activeFinding,\n registration,\n defaultRegistration,\n onRegistrationChange,\n registrationMode = 'off',\n autoDetect,\n onDetectStatus,\n allowCapture = false,\n captureFacingMode = 'user',\n onCapture,\n legend = false,\n ariaLabel,\n className,\n },\n ref,\n ) => {\n const { t } = useTranslation();\n const reactId = useId();\n const instanceId = id ?? reactId;\n const rootRef = useRef<HTMLDivElement>(null);\n const svgRef = useRef<SVGSVGElement>(null);\n const videoRef = useRef<HTMLVideoElement>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n // The persistent Take/Retake button — focus re-homes here when the\n // streaming viewfinder (which held focus) unmounts on capture / cancel.\n const captureButtonRef = useRef<HTMLButtonElement>(null);\n const unmountedRef = useRef(false);\n\n /* ---- Active view (controlled / uncontrolled, radio-driven) ---- */\n const isViewControlled = viewProp !== undefined;\n const [internalView, setInternalView] = useState<FaceViewId>(\n viewProp ?? defaultView,\n );\n const viewId = isViewControlled ? viewProp : internalView;\n const viewIdRef = useRef<FaceViewId>(viewId);\n viewIdRef.current = viewId;\n const setView = useCallback(\n (next: FaceViewId) => {\n if (!isViewControlled) setInternalView(next);\n onViewChange?.(next);\n },\n [isViewControlled, onViewChange],\n );\n\n const view = FACE_VIEWS[viewId];\n const [, , vbW, vbH] = view.viewBox.split(' ').map(Number) as [\n number,\n number,\n number,\n number,\n ];\n const interactive = mode === 'interactive';\n const adjusting = interactive && registrationMode === 'adjust';\n\n /* ---- Findings state (controlled / uncontrolled) ---- */\n const isControlled = value !== undefined;\n const [internal, setInternal] = useState<FaceChart>(defaultValue ?? {});\n const chart = isControlled ? value : internal;\n const chartRef = useRef<FaceChart>(chart);\n chartRef.current = chart;\n\n const commitChart = useCallback(\n (next: FaceChart) => {\n if (!isControlled) setInternal(next);\n onChange?.(next);\n },\n [isControlled, onChange],\n );\n\n const addFinding = useCallback(\n (zoneId: string, type: FaceFindingType, note?: string) => {\n const current = chartRef.current[zoneId] ?? [];\n if (current.some((f) => f.type === type)) return;\n commitChart({\n ...chartRef.current,\n [zoneId]: [...current, { type, note }],\n });\n },\n [commitChart],\n );\n const removeFindingFn = useCallback(\n (zoneId: string, type: FaceFindingType) => {\n const current = chartRef.current[zoneId] ?? [];\n const next = current.filter((f) => f.type !== type);\n const copy = { ...chartRef.current };\n if (next.length > 0) copy[zoneId] = next;\n else delete copy[zoneId];\n commitChart(copy);\n },\n [commitChart],\n );\n const toggleFinding = useCallback(\n (zoneId: string, type: FaceFindingType) => {\n const has = (chartRef.current[zoneId] ?? []).some(\n (f) => f.type === type,\n );\n if (has) removeFindingFn(zoneId, type);\n else addFinding(zoneId, type);\n },\n [addFinding, removeFindingFn],\n );\n\n /* ---- Registration state (controlled / uncontrolled) ---- */\n const isRegControlled = registration !== undefined;\n const [internalReg, setInternalReg] = useState<FaceRegistration>(\n defaultRegistration ?? {},\n );\n const regMap = isRegControlled ? registration : internalReg;\n const activeAffine = regMap[viewId] ?? IDENTITY_AFFINE;\n const affineRef = useRef<FaceAffine>(activeAffine);\n affineRef.current = activeAffine;\n // Mirror of the full registration map so commits never read a stale closure.\n const affineRefMap = useRef<FaceRegistration>(regMap);\n affineRefMap.current = regMap;\n\n const commitReg = useCallback(\n (m: FaceAffine) => {\n const next = { ...affineRefMap.current, [viewId]: m };\n if (!isRegControlled) setInternalReg(next);\n onRegistrationChange?.(next);\n },\n [isRegControlled, onRegistrationChange, viewId],\n );\n\n const parts = useMemo(\n () => decomposeAffine(activeAffine, view.pivot),\n [activeAffine, view.pivot],\n );\n\n // The right-profile mesh is flipped about the frame centre at render time,\n // so landmark photo points (detector output, drag targets) must be flipped\n // to / from the un-mirrored space the affine is solved in. Self-inverse.\n const mirrorPt = useCallback(\n (p: Point): Point => (view.mirror ? { x: vbW - p.x, y: p.y } : p),\n [view.mirror, vbW],\n );\n\n /* ---- Landmark handle targets (pointer fitting) ---- */\n const [targets, setTargets] = useState<Point[]>(() =>\n view.landmarks.map((lm) => mirrorPt(applyAffine(activeAffine, lm))),\n );\n // Keep handles riding the mesh when the affine changes for any reason other\n // than an in-progress drag (slider, auto-fit, reset, external prop).\n const draggingRef = useRef<number | null>(null);\n useEffect(() => {\n if (draggingRef.current !== null) return;\n setTargets(\n view.landmarks.map((lm) => mirrorPt(applyAffine(activeAffine, lm))),\n );\n }, [activeAffine, view.landmarks, mirrorPt]);\n\n const fitFromPairs = useCallback(\n (pairs: { id: string; photo: Point }[]) => {\n const src: Point[] = [];\n const dst: Point[] = [];\n for (const pair of pairs) {\n const anchor = view.landmarks.find((l) => l.id === pair.id);\n if (!anchor) continue;\n src.push({ x: anchor.x, y: anchor.y });\n dst.push(mirrorPt(pair.photo));\n }\n if (src.length >= 3) commitReg(solveAffine(src, dst));\n },\n [commitReg, view.landmarks, mirrorPt],\n );\n\n const toSvgPoint = useCallback(\n (clientX: number, clientY: number): Point => {\n const svg = svgRef.current;\n if (!svg) return { x: 0, y: 0 };\n const pt = svg.createSVGPoint();\n pt.x = clientX;\n pt.y = clientY;\n const ctm = svg.getScreenCTM();\n if (!ctm) return { x: 0, y: 0 };\n const local = pt.matrixTransform(ctm.inverse());\n return { x: local.x, y: local.y };\n },\n [],\n );\n\n const handlePointerDown = useCallback(\n (index: number) => (e: ReactPointerEvent<SVGCircleElement>) => {\n draggingRef.current = index;\n e.currentTarget.setPointerCapture(e.pointerId);\n },\n [],\n );\n const handlePointerMove = useCallback(\n (e: ReactPointerEvent<SVGCircleElement>) => {\n const index = draggingRef.current;\n if (index === null) return;\n const p = toSvgPoint(e.clientX, e.clientY);\n const next = targets.map((tg, i) => (i === index ? p : tg));\n setTargets(next);\n const src = view.landmarks.map((lm) => ({ x: lm.x, y: lm.y }));\n // `next` are rendered (possibly mirrored) positions — solve in\n // un-mirrored space so the mirror transform lands them back correctly.\n commitReg(solveAffine(src, next.map(mirrorPt)));\n },\n [targets, toSvgPoint, view.landmarks, commitReg, mirrorPt],\n );\n const handlePointerUp = useCallback(() => {\n draggingRef.current = null;\n }, []);\n\n /* ---- Capture (on-device camera + file fallback) ---- */\n const [captureState, setCaptureState] = useState<FaceCaptureState>('idle');\n const [captureError, setCaptureError] = useState<FaceCaptureError | null>(\n null,\n );\n // Captured frames are keyed by view — each view keeps its own photo.\n const [capturedByView, setCapturedByView] = useState<\n Partial<Record<FaceViewId, string>>\n >({});\n // Per-view photo: explicit map wins, else `patientPhoto` is the front shorthand.\n const viewPhoto =\n viewPhotos?.[viewId] ?? (viewId === 'front' ? patientPhoto : undefined);\n // The captured frame supersedes the prop photo (uncontrolled-friendly).\n const effectiveSrc = capturedByView[viewId] ?? viewPhoto?.src;\n\n // Stream teardown — the single most important privacy path.\n const stopStream = useCallback(() => {\n streamRef.current?.getTracks().forEach((track) => track.stop());\n streamRef.current = null;\n const video = videoRef.current;\n if (video) video.srcObject = null;\n }, []);\n\n const cancelCapture = useCallback(() => {\n stopStream();\n setCaptureState('idle');\n }, [stopStream]);\n\n // Start the camera — user gesture only, never auto on mount.\n const startCapture = useCallback(async () => {\n if (!allowCapture) return;\n if (!mediaDevicesAvailable()) {\n setCaptureError('insecure-context');\n setCaptureState('error');\n return;\n }\n setCaptureError(null);\n setCaptureState('requesting');\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n video: { facingMode: captureFacingMode },\n audio: false,\n });\n // Guard an unmount between the permission prompt and resolution.\n if (unmountedRef.current) {\n stream.getTracks().forEach((track) => track.stop());\n return;\n }\n streamRef.current = stream;\n const video = videoRef.current;\n if (video) {\n try {\n video.srcObject = stream;\n } catch {\n /* some envs reject a non-native MediaStream on srcObject; the\n stream is still held + torn down correctly */\n }\n const playResult = video.play?.();\n if (playResult && typeof playResult.catch === 'function') {\n playResult.catch(() => {\n /* autoplay can reject silently; the Capture button still works */\n });\n }\n }\n setCaptureState('streaming');\n } catch (err: unknown) {\n stopStream();\n setCaptureError(classifyMediaError(err));\n setCaptureState('error');\n }\n }, [allowCapture, captureFacingMode, stopStream]);\n\n const commitCaptured = useCallback(\n (src: string, width: number, height: number) => {\n setCapturedByView((prev) => ({ ...prev, [viewId]: src }));\n onCapture?.({ src, width, height, view: viewId });\n },\n [onCapture, viewId],\n );\n\n // Freeze the current frame onto a canvas, release the camera, emit it.\n const capturePhoto = useCallback(() => {\n const video = videoRef.current;\n const vw = video?.videoWidth || video?.clientWidth || 0;\n const vh = video?.videoHeight || video?.clientHeight || 0;\n if (!video || video.readyState < 2 || vw === 0 || vh === 0) {\n setCaptureError('capture-failed');\n setCaptureState('error');\n return;\n }\n const canvas = document.createElement('canvas');\n canvas.width = vw;\n canvas.height = vh;\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n setCaptureError('capture-failed');\n setCaptureState('error');\n return;\n }\n try {\n ctx.drawImage(video, 0, 0, vw, vh);\n } catch {\n setCaptureError('capture-failed');\n setCaptureState('error');\n return;\n }\n // Release the camera the instant the frame is frozen — LED goes dark.\n stopStream();\n commitCaptured(canvas.toDataURL('image/jpeg', 0.92), vw, vh);\n setCaptureState('idle');\n }, [stopStream, commitCaptured]);\n\n // File fallback (and the desktop path where no camera is wired up).\n const onPickFile = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n e.target.value = '';\n if (!file) return;\n const reader = new FileReader();\n reader.onload = () => {\n const src = typeof reader.result === 'string' ? reader.result : '';\n if (!src) return;\n const img = new Image();\n img.onload = () =>\n commitCaptured(src, img.naturalWidth, img.naturalHeight);\n img.onerror = () => commitCaptured(src, vbW, vbH);\n img.src = src;\n };\n reader.readAsDataURL(file);\n },\n [commitCaptured, vbW, vbH],\n );\n\n // Privacy: tear the camera down on unmount, whatever the phase.\n useEffect(() => {\n unmountedRef.current = false;\n return () => {\n unmountedRef.current = true;\n streamRef.current?.getTracks().forEach((track) => track.stop());\n streamRef.current = null;\n };\n }, []);\n\n const captureStreaming = captureState === 'streaming';\n\n // When the viewfinder tears down (capture / cancel), it unmounts the button\n // that held focus — re-home focus to the persistent Take/Retake button so a\n // keyboard user isn't dropped to <body>.\n const wasStreamingRef = useRef(false);\n useEffect(() => {\n if (wasStreamingRef.current && !captureStreaming) {\n captureButtonRef.current?.focus();\n }\n wasStreamingRef.current = captureStreaming;\n }, [captureStreaming]);\n\n /* ---- Detection ---- */\n const [detectStatus, setDetectStatus] = useState<FaceDetectStatus>('idle');\n const setStatus = useCallback(\n (s: FaceDetectStatus) => {\n setDetectStatus(s);\n onDetectStatus?.(s);\n },\n [onDetectStatus],\n );\n const runDetect = useCallback(async (): Promise<boolean> => {\n if (!autoDetect || !effectiveSrc || view.landmarks.length === 0)\n return false;\n const options = typeof autoDetect === 'object' ? autoDetect : {};\n const onStage = (stage: 'loading' | 'detecting') =>\n setStatus(stage === 'detecting' ? 'detecting' : 'loading');\n try {\n setStatus('loading');\n // FRONT: MediaPipe FaceMesh (accurate, non-collinear eyes/nose/chin).\n // PROFILE: MediaPipe collapses a 90° pose, so use YuNet and fit the mesh\n // on its visible-side EYE / NOSE-TIP / MOUTH triangle.\n let pairs: Array<{ id: string; photo: Point }> | null;\n if (view.id === 'front') {\n pairs = await detectFaceLandmarks(effectiveSrc, {\n viewWidth: vbW,\n viewHeight: vbH,\n onStage,\n wasmBaseUrl: options.wasmBaseUrl,\n modelAssetPath: options.modelAssetPath,\n });\n } else {\n const raw = await detectFaceYuNet(effectiveSrc, {\n viewWidth: vbW,\n viewHeight: vbH,\n onStage,\n ortModuleUrl: options.ortModuleUrl,\n ortWasmBaseUrl: options.ortWasmBaseUrl,\n modelUrl: options.yunetModelUrl,\n });\n pairs = raw && profileTriangleFromYuNet(raw);\n }\n if (!pairs || pairs.length < 3) {\n setStatus('no-face');\n return false;\n }\n fitFromPairs(pairs);\n setStatus('done');\n return true;\n } catch {\n setStatus('error');\n return false;\n }\n }, [\n autoDetect,\n effectiveSrc,\n view.id,\n view.landmarks,\n vbW,\n vbH,\n setStatus,\n fitFromPairs,\n ]);\n\n /* ---- Imperative handle + agent registration ---- */\n const focusZoneEl = useCallback((zoneId: string) => {\n rootRef.current\n ?.querySelector<SVGGElement>(`[data-zone=\"${zoneId}\"]`)\n ?.focus();\n }, []);\n\n const handle = useMemo<FaceSchemeHandle>(\n () => ({\n focusZone: focusZoneEl,\n getChart: () => chartRef.current,\n getZone: (zoneId) => chartRef.current[zoneId] ?? [],\n zonesWith: (type) =>\n Object.entries(chartRef.current)\n .filter(([, findings]) => findings.some((f) => f.type === type))\n .map(([zoneId]) => zoneId),\n setFinding: addFinding,\n removeFinding: removeFindingFn,\n clearZone: (zoneId) => {\n if (!chartRef.current[zoneId]) return;\n const copy = { ...chartRef.current };\n delete copy[zoneId];\n commitChart(copy);\n },\n getRegistration: () => affineRef.current,\n setRegistration: (m) => commitReg(m),\n resetRegistration: () => commitReg(IDENTITY_AFFINE),\n fitToLandmarks: fitFromPairs,\n detect: runDetect,\n startCapture: () => void startCapture(),\n cancelCapture,\n getView: () => viewIdRef.current,\n setView,\n }),\n [\n focusZoneEl,\n addFinding,\n removeFindingFn,\n commitChart,\n commitReg,\n fitFromPairs,\n runDetect,\n startCapture,\n cancelCapture,\n setView,\n ],\n );\n useImperativeHandle(ref, () => handle, [handle]);\n useAgentRegistration(faceSchemeAgent, handle, instanceId);\n\n /* ---- Zone label ---- */\n const zoneLabel = useCallback(\n (zone: FaceZone): string => {\n const name = t(`faceScheme.zone.${zone.labelKey}`);\n const findings = chartRef.current[zone.id] ?? [];\n if (findings.length === 0) return name;\n const findingNames = findings\n .map((f) => t(`faceScheme.finding.${f.type}`))\n .join(', ');\n return t('faceScheme.zoneWithFindings', {\n zone: name,\n findings: findingNames,\n });\n },\n [t],\n );\n\n const handleActivate = useCallback(\n (zone: FaceZone) => {\n onSelectZone?.(zone.id);\n if (interactive && activeFinding) toggleFinding(zone.id, activeFinding);\n },\n [interactive, activeFinding, toggleFinding, onSelectZone],\n );\n\n const photoAlt = viewPhoto?.altKey\n ? t(viewPhoto.altKey)\n : t('faceScheme.photo.defaultAlt');\n\n // Right profile = left mesh mirrored horizontally about the frame centre.\n const meshTransform = view.mirror\n ? `translate(${vbW} 0) scale(-1 1) ${affineToString(activeAffine)}`\n : affineToString(activeAffine);\n\n const updatePart = useCallback(\n (field: SliderField, val: number) => {\n commitReg(composeAffine({ ...parts, [field]: val }, view.pivot));\n },\n [commitReg, parts, view.pivot],\n );\n\n // Auto-detect runs wherever the active view has fit landmarks (all three\n // views do — the front uses eye/nose/chin, the profiles use midline\n // cephalometric points the detector tracks even on a 90° profile).\n const detectEnabled =\n Boolean(autoDetect) && Boolean(effectiveSrc) && view.landmarks.length > 0;\n // Capture is its own opt-in — available regardless of `mode` so the\n // practitioner can (re)take the photo on any surface that allows it.\n const captureEnabled = allowCapture;\n\n /* ---- Render ---- */\n return (\n <div\n ref={rootRef}\n data-component=\"face-scheme\"\n data-component-id={instanceId}\n data-view={viewId}\n className={['ds:flex ds:flex-col ds:gap-[var(--spacing-md)]', className]\n .filter(Boolean)\n .join(' ')}\n role=\"group\"\n aria-label={ariaLabel ?? t('faceScheme.ariaLabel')}\n >\n {/* View switcher — front / left profile / right profile. */}\n {showViewSwitcher && FACE_VIEW_IDS.length > 1 && (\n <RadioGroup\n label={t('faceScheme.viewSwitcherLabel')}\n variant=\"horizontal\"\n value={viewId}\n onValueChange={(v) => setView(v as FaceViewId)}\n >\n {FACE_VIEW_IDS.map((vid) => (\n <Radio\n key={vid}\n value={vid}\n label={t(`faceScheme.view.${FACE_VIEWS[vid].labelKey}`)}\n />\n ))}\n </RadioGroup>\n )}\n\n {/* Live camera viewfinder — replaces the figure while streaming. */}\n {captureStreaming && (\n <div\n data-testid=\"face-scheme-viewfinder\"\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n >\n {/* eslint-disable-next-line jsx-a11y/media-has-caption -- live self-view, not media content */}\n <video\n ref={videoRef}\n autoPlay\n playsInline\n muted\n aria-label={t('faceScheme.capture.viewfinder')}\n className=\"ds:mx-auto ds:block ds:w-full ds:max-w-[28rem] ds:[border-radius:var(--radius-md)] ds:[transform:scaleX(-1)]\"\n />\n <div className=\"ds:flex ds:flex-wrap ds:justify-center ds:gap-[var(--spacing-xs)]\">\n <Button size=\"sm\" intent=\"primary\" onClick={() => capturePhoto()}>\n {t('faceScheme.capture.capture')}\n </Button>\n <Button size=\"sm\" intent=\"outline\" onClick={cancelCapture}>\n {t('faceScheme.capture.cancel')}\n </Button>\n </div>\n </div>\n )}\n\n {!captureStreaming && (\n <RadixTooltip.Provider delayDuration={200}>\n <svg\n ref={svgRef}\n data-testid=\"face-scheme-svg\"\n viewBox={view.viewBox}\n role=\"group\"\n aria-label={t(`faceScheme.view.${view.labelKey}`)}\n className=\"ds:mx-auto ds:block ds:h-auto ds:w-full ds:max-w-[28rem] ds:touch-none ds:[border-radius:var(--radius-md)]\"\n >\n {effectiveSrc && (\n <image\n href={effectiveSrc}\n x={0}\n y={0}\n width={vbW}\n height={vbH}\n preserveAspectRatio=\"xMidYMid slice\"\n role=\"img\"\n aria-label={photoAlt}\n />\n )}\n\n {/* Registered mesh — the affine maps the canonical zones onto the photo. */}\n <g transform={meshTransform}>\n {/* The dashed face-contour is a guide for the photoless schematic;\n with a photo the real face outline is visible, so drop it. */}\n {!effectiveSrc && (\n <path\n d={view.contour.d}\n aria-hidden=\"true\"\n className=\"ds:[fill:none] ds:[stroke:var(--accent)] ds:[stroke-opacity:0.4] ds:[stroke-width:1] ds:[vector-effect:non-scaling-stroke] ds:[stroke-dasharray:4_5]\"\n />\n )}\n {view.zones.map((zone) => {\n const findings = chart[zone.id] ?? [];\n const marked = findings.length > 0;\n return (\n <RadixTooltip.Root key={zone.id}>\n <RadixTooltip.Trigger asChild>\n <g\n data-zone={zone.id}\n data-side={zone.side}\n role={interactive || onSelectZone ? 'button' : 'img'}\n aria-label={zoneLabel(zone)}\n aria-pressed={\n interactive && activeFinding ? marked : undefined\n }\n // Suppress the UA focus box on the <g>; draw the ring on the\n // zone path itself. forced-colors re-asserts a system stroke.\n className=\"ds:outline-none ds:[&:focus-visible>*:first-child]:[stroke:var(--ring)] ds:[&:focus-visible>*:first-child]:[stroke-width:var(--focus-ring-width)] ds:forced-colors:[&:focus-visible>*:first-child]:[stroke:CanvasText]\"\n tabIndex={interactive || onSelectZone ? 0 : -1}\n onClick={() => handleActivate(zone)}\n onKeyDown={(e: KeyboardEvent<SVGGElement>) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n handleActivate(zone);\n }\n }}\n >\n <path\n d={zone.d}\n className={zoneVariants({ interactive, marked })}\n />\n {findings.map((f, i) => {\n const m = FACE_FINDING_MARKER[f.type];\n const offset = (i - (findings.length - 1) / 2) * 26;\n return (\n <path\n key={f.type}\n d={markerPath(\n m.shape,\n zone.cx + offset,\n zone.cy,\n 11,\n )}\n aria-hidden=\"true\"\n className={markerToneFill[m.tone]}\n />\n );\n })}\n </g>\n </RadixTooltip.Trigger>\n <RadixTooltip.Portal>\n <RadixTooltip.Content\n sideOffset={4}\n className=\"ds:z-50 ds:rounded-[var(--radius-sm)] ds:bg-[var(--popover)] ds:px-2 ds:py-1 ds:text-[length:var(--font-size-xs)] ds:text-[var(--popover-foreground)] ds:shadow-[var(--shadow-md)]\"\n >\n {zoneLabel(zone)}\n <RadixTooltip.Arrow className=\"ds:[fill:var(--popover)]\" />\n </RadixTooltip.Content>\n </RadixTooltip.Portal>\n </RadixTooltip.Root>\n );\n })}\n </g>\n\n {/* Landmark handles — pointer-only fitting affordance (the numeric\n sliders below are the accessible, keyboard-operable equivalent). */}\n {adjusting && effectiveSrc && (\n <g aria-hidden=\"true\" data-testid=\"face-scheme-handles\">\n {view.landmarks.map((lm, i) => {\n const tg =\n targets[i] ?? mirrorPt(applyAffine(activeAffine, lm));\n return (\n <g key={lm.id}>\n <circle\n cx={tg.x}\n cy={tg.y}\n r={13}\n data-handle={lm.id}\n onPointerDown={handlePointerDown(i)}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n className=\"ds:[fill:var(--background)] ds:[fill-opacity:0.9] ds:[stroke:var(--accent)] ds:[stroke-width:2.5] ds:[vector-effect:non-scaling-stroke] ds:cursor-grab ds:touch-none\"\n />\n <path\n d={`M ${tg.x - 5} ${tg.y} H ${tg.x + 5} M ${tg.x} ${tg.y - 5} V ${tg.y + 5}`}\n className=\"ds:[stroke:var(--accent)] ds:[stroke-width:2] ds:[vector-effect:non-scaling-stroke]\"\n pointerEvents=\"none\"\n />\n </g>\n );\n })}\n </g>\n )}\n </svg>\n </RadixTooltip.Provider>\n )}\n\n {/* Capture controls — on-device camera + file fallback. */}\n {captureEnabled && (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n <div className=\"ds:flex ds:flex-wrap ds:justify-center ds:gap-[var(--spacing-xs)]\">\n <Button\n ref={captureButtonRef}\n size=\"sm\"\n intent={effectiveSrc ? 'outline' : 'primary'}\n onClick={() => void startCapture()}\n disabled={captureState === 'requesting'}\n >\n {t(\n effectiveSrc\n ? 'faceScheme.capture.retake'\n : 'faceScheme.capture.take',\n )}\n </Button>\n <Button\n size=\"sm\"\n intent=\"outline\"\n onClick={() => fileInputRef.current?.click()}\n >\n {t('faceScheme.capture.upload')}\n </Button>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n capture={captureFacingMode}\n onChange={onPickFile}\n className=\"ds:sr-only\"\n aria-hidden=\"true\"\n tabIndex={-1}\n data-testid=\"face-scheme-file-input\"\n />\n </div>\n {/* Always mounted so a polite region exists before its first message\n — some SRs miss a live region created in the same mutation as its\n initial text (mirrors the detector-status region below). */}\n <span\n role=\"status\"\n aria-live=\"polite\"\n className=\"ds:text-center ds:text-[length:var(--font-size-xs)] ds:text-[var(--destructive)] ds:empty:hidden\"\n >\n {captureState === 'error' && captureError\n ? t(CAPTURE_ERROR_KEY[captureError])\n : ''}\n </span>\n </div>\n )}\n\n {/* Registration controls — accessible numeric path + actions. */}\n {adjusting && (\n <div\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n data-testid=\"face-scheme-registration\"\n >\n <p className=\"ds:m-0 ds:text-[length:var(--font-size-xs)] ds:text-[var(--muted-foreground)]\">\n {t('faceScheme.registration.hint')}\n </p>\n <div className=\"ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-xs)] ds:sm:grid-cols-2\">\n {SLIDER_FIELDS.map((field) => {\n const range = SLIDER_RANGES[field];\n const isScale = field === 'scaleX' || field === 'scaleY';\n return (\n <div\n key={field}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)] ds:text-[length:var(--font-size-xs)] ds:text-[var(--foreground)]\"\n >\n <span>{t(`faceScheme.registration.${field}`)}</span>\n <Slider\n aria-label={t(`faceScheme.registration.${field}`)}\n value={[parts[field]]}\n min={range.min}\n max={range.max}\n step={range.step}\n formatValue={(v) =>\n isScale\n ? `${Math.round(v * 100)}%`\n : field === 'rotation' || field === 'shear'\n ? `${v.toFixed(1)}°`\n : `${Math.round(v)}`\n }\n onValueChange={(vals) => {\n const v = vals[0];\n if (typeof v === 'number') updatePart(field, v);\n }}\n />\n </div>\n );\n })}\n </div>\n <div className=\"ds:flex ds:flex-wrap ds:gap-[var(--spacing-xs)]\">\n {detectEnabled && (\n <Button\n size=\"sm\"\n intent=\"primary\"\n onClick={() => void runDetect()}\n disabled={\n detectStatus === 'loading' || detectStatus === 'detecting'\n }\n >\n {t('faceScheme.registration.autoDetect')}\n </Button>\n )}\n <Button\n size=\"sm\"\n intent=\"outline\"\n onClick={() => commitReg(IDENTITY_AFFINE)}\n >\n {t('faceScheme.registration.reset')}\n </Button>\n {/* Always mounted so a polite region exists before its first\n message — some SRs miss a live region created in the same\n mutation as its initial text. */}\n {detectEnabled && (\n <span\n role=\"status\"\n aria-live=\"polite\"\n className=\"ds:self-center ds:text-[length:var(--font-size-xs)] ds:text-[var(--muted-foreground)]\"\n >\n {detectStatus === 'idle'\n ? ''\n : t(`faceScheme.detect.${detectStatus}`)}\n </span>\n )}\n </div>\n </div>\n )}\n\n {legend && (\n <ul\n aria-label={t('faceScheme.legendLabel')}\n className=\"ds:flex ds:flex-wrap ds:gap-x-4 ds:gap-y-1 ds:p-0 ds:text-[length:var(--font-size-xs)] ds:text-[var(--muted-foreground)]\"\n >\n {FACE_FINDING_TYPES.map((type) => {\n const m = FACE_FINDING_MARKER[type];\n return (\n <li key={type} className=\"ds:flex ds:list-none ds:items-center\">\n <AnnotationMarker\n shape={m.shape}\n tone={m.tone}\n size=\"sm\"\n label={t(`faceScheme.finding.${type}`)}\n />\n </li>\n );\n })}\n </ul>\n )}\n </div>\n );\n },\n);\n\nFaceScheme.displayName = 'FaceScheme';\n"],"names":["ZONE_SCHEMA","VIEW_SCHEMA","FINDING_SCHEMA","faceSchemeAgent","handle","zone","findings","f","args","DEFAULT_VISION_MODULE","DEFAULT_WASM_BASE","DEFAULT_MODEL","ANCHOR_INDICES","landmarkerCache","getLandmarker","visionModuleUrl","wasmBaseUrl","modelAssetPath","onStage","key","cached","promise","mod","fileset","loadImage","src","img","detectFaceLandmarks","options","viewWidth","viewHeight","image","landmarker","face","imgW","imgH","scale","offsetX","offsetY","toView","lm","pairs","id","indices","sx","sy","count","idx","v","DEFAULT_ORT_MODULE","DEFAULT_ORT_WASM_BASE","NET_SIZE","STRIDES","KEYPOINT_IDS","sessionCache","getSession","ortModuleUrl","ortWasmBaseUrl","modelUrl","ort","model","session","defaultModelUrl","preprocess","Tensor","w","h","dw","dh","padX","padY","canvas","ctx","data","n","i","sigmoid","x","decode","out","scoreThreshold","needSigmoid","probe","best","cls","obj","kps","cols","rows","cv","ov","score","c","r","keypoints","j","detectFaceYuNet","tensor","result","coverScale","kx","ky","px","py","FRONT_VIEWBOX","FRONT_PIVOT","FRONT_CONTOUR","FRONT_ZONES","FRONT_LANDMARKS","PROFILE_ZONE_PATHS","PROFILE_CONTOUR","z","PROFILE_ZONES","PROFILE_LANDMARKS","FACE_VIEWS","FACE_VIEW_IDS","FACE_FINDING_TYPES","FACE_FINDING_MARKER","IDENTITY_AFFINE","affineToString","m","applyAffine","p","DEG","composeAffine","parts","pivot","rad","ca","sa","k","a","b","d","e","decomposeAffine","scaleX","rotation","det","scaleY","shear","translateX","translateY","solveAffine","dst","Sxx","Sxy","Sx","Syy","Sy","bx0","bx1","bx2","by0","by1","by2","s","t","i00","i01","i02","i11","i12","i22","solve","r0","r1","r2","profileTriangleFromYuNet","photoOf","_a","nose","rightEye","leftEye","rightMouth","leftMouth","farther","mediaDevicesAvailable","classifyMediaError","err","name","CAPTURE_ERROR_KEY","markerToneFill","zoneVariants","cva","SLIDER_RANGES","SLIDER_FIELDS","FaceScheme","forwardRef","patientPhoto","viewPhotos","viewProp","defaultView","onViewChange","showViewSwitcher","mode","value","defaultValue","onChange","onSelectZone","activeFinding","registration","defaultRegistration","onRegistrationChange","registrationMode","autoDetect","onDetectStatus","allowCapture","captureFacingMode","onCapture","legend","ariaLabel","className","ref","useTranslation","reactId","useId","instanceId","rootRef","useRef","svgRef","videoRef","streamRef","fileInputRef","captureButtonRef","unmountedRef","isViewControlled","internalView","setInternalView","useState","viewId","viewIdRef","setView","useCallback","next","view","vbW","vbH","interactive","adjusting","isControlled","internal","setInternal","chart","chartRef","commitChart","addFinding","zoneId","type","note","current","removeFindingFn","copy","toggleFinding","isRegControlled","internalReg","setInternalReg","regMap","activeAffine","affineRef","affineRefMap","commitReg","useMemo","mirrorPt","targets","setTargets","draggingRef","useEffect","fitFromPairs","pair","anchor","l","toSvgPoint","clientX","clientY","svg","pt","ctm","local","handlePointerDown","index","handlePointerMove","tg","handlePointerUp","captureState","setCaptureState","captureError","setCaptureError","capturedByView","setCapturedByView","viewPhoto","effectiveSrc","stopStream","track","video","cancelCapture","startCapture","stream","playResult","commitCaptured","width","height","prev","capturePhoto","vw","vh","onPickFile","file","reader","captureStreaming","wasStreamingRef","detectStatus","setDetectStatus","setStatus","runDetect","stage","raw","focusZoneEl","_b","useImperativeHandle","useAgentRegistration","zoneLabel","findingNames","handleActivate","photoAlt","meshTransform","updatePart","field","val","detectEnabled","captureEnabled","jsxs","jsx","RadioGroup","vid","Radio","Button","RadixTooltip","marked","offset","markerPath","range","isScale","Slider","vals","AnnotationMarker"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "packageVersion": "0.72.0",
3
+ "packageVersion": "0.72.2",
4
4
  "components": [
5
5
  {
6
6
  "kind": "component",
@@ -1,4 +1,4 @@
1
- import { F as s, a as f, b as A, c as F, d as I, I as E, e as c, f as n, g as o, h as _, i, j as m, s as N } from "../../_chunks/face-scheme-DOZO3M_c.js";
1
+ import { F as s, a as f, b as A, c as F, d as I, I as E, e as c, f as n, g as o, h as _, i, j as m, s as N } from "../../_chunks/face-scheme-BT9_KdL0.js";
2
2
  export {
3
3
  s as FACE_FINDING_MARKER,
4
4
  f as FACE_FINDING_TYPES,
package/dist/index.js CHANGED
@@ -199,7 +199,7 @@ import { S as Cf, s as xf } from "./_chunks/signature-capture-Ct_4Md4e.js";
199
199
  import { S as uf, s as Tf } from "./_chunks/signature-field-jgwYgUhO.js";
200
200
  import { S as If, s as bf } from "./_chunks/sign-document-nK03S73h.js";
201
201
  import { S as Rf, f as _f, i as Nf } from "./_chunks/slot-grid-FsqumOaS.js";
202
- import { F as Ff, a as Mf, b as Df, c as Lf, d as yf, I as Of, e as Bf, f as kf, g as Hf, h as vf, i as Gf, j as Vf, s as wf } from "./_chunks/face-scheme-DOZO3M_c.js";
202
+ import { F as Ff, a as Mf, b as Df, c as Lf, d as yf, I as Of, e as Bf, f as kf, g as Hf, h as vf, i as Gf, j as Vf, s as wf } from "./_chunks/face-scheme-BT9_KdL0.js";
203
203
  import { C as Uf, a as Yf, b as Kf, c as Xf, F as qf, d as jf, e as zf, P as Qf, f as Jf, S as Zf, g as $f, h as aA, T as eA, i as rA, j as sA, k as oA, l as tA, m as nA, n as iA, o as pA, p as mA, s as lA, q as cA, r as dA, t as fA, u as AA, v as gA, z as CA, w as xA, x as SA } from "./_chunks/tooth-scheme-D_rso4uu.js";
204
204
  import { T as TA } from "./_chunks/transaction-chip-B6iWoVmU.js";
205
205
  import { W as IA, a as bA } from "./_chunks/whatsapp-button-BIy6NaJK.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfadocs/ui-kit-debug",
3
- "version": "0.72.0",
3
+ "version": "0.72.2",
4
4
  "type": "module",
5
5
  "description": "AlfaDocs shared design system — tokens, components, patterns, and translations for platform, booking, and alfascribe. (debug build — identical runtime to @alfadocs/ui-kit, ships source maps for symbolication).",
6
6
  "license": "BUSL-1.1",
@@ -1 +0,0 @@
1
- {"version":3,"file":"face-scheme-DOZO3M_c.js","sources":["../../src/components/face-scheme/face-scheme.agent.ts","../../src/components/face-scheme/detect-face-landmarks.ts","../../src/components/face-scheme/detect-face-yunet.ts","../../src/components/face-scheme/face-scheme-zones.ts","../../src/components/face-scheme/face-scheme-data.ts","../../src/components/face-scheme/face-scheme.tsx"],"sourcesContent":["import type { AgentAdapter } from '../../agent/types';\nimport type { FaceSchemeHandle } from './face-scheme';\nimport type { FaceFindingType, FaceViewId } from './face-scheme-data';\n\nconst ZONE_SCHEMA = {\n type: 'string',\n description:\n \"Face-zone id (e.g. 'glabella', 'nasolabialFold', 'forehead', 'mandibularAngleLeft' on the front view; 'cheekbone', 'masseter', 'chin' on a profile view).\",\n} as const;\n\nconst VIEW_SCHEMA = {\n type: 'string',\n enum: ['front', 'profile-left', 'profile-right'],\n description: \"Active view id: 'front', 'profile-left', or 'profile-right'.\",\n} as const;\n\nconst FINDING_SCHEMA = {\n enum: ['observation', 'treatment', 'concern', 'note'],\n} as const;\n\nexport const faceSchemeAgent: AgentAdapter<FaceSchemeHandle> = {\n id: 'face-scheme',\n // pick — focus a zone (transient); set_value / edit_inline — record / clear\n // findings on a zone; view_change — switch the active view (front / left /\n // right profile), since each view renders a different zone set. Registration\n // (the photo fit) is exposed only as the auto-align action.\n capabilities: ['pick', 'set_value', 'edit_inline', 'view_change'],\n state: {\n activeView: {\n // Which view is mounted — it determines which zones are in the DOM, so an\n // agent must read this before focus_zone on a profile-only zone.\n type: 'string',\n descriptionKey: 'ui.agent.faceScheme.state.activeView',\n description:\n 'Id of the currently displayed view (front / profile-left / profile-right).',\n read: (handle) => handle.getView(),\n },\n chart: {\n // A9: identifiers + finding ENUMS only — never the free-text finding\n // `note` (PHI) and never the patient-photo `src`. Notes are written but\n // never read back through the snapshot.\n type: 'Record<string, string[]>',\n descriptionKey: 'ui.agent.faceScheme.state.chart',\n description:\n 'Current face chart — the finding types recorded on each zone, keyed by zone id. Free-text notes and the patient photo are not exposed here.',\n read: (handle) =>\n Object.fromEntries(\n Object.entries(handle.getChart()).map(([zone, findings]) => [\n zone,\n findings.map((f) => f.type),\n ]),\n ),\n },\n markedZones: {\n type: 'string[]',\n descriptionKey: 'ui.agent.faceScheme.state.markedZones',\n description: 'Zone ids carrying at least one finding.',\n read: (handle) => Object.keys(handle.getChart()),\n },\n },\n actions: {\n focus_zone: {\n safety: 'read',\n argsType: '{ id: string }',\n argsSchema: {\n type: 'object',\n properties: { id: ZONE_SCHEMA },\n required: ['id'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.faceScheme.actions.focusZone',\n description: 'Move focus to a specific face zone by id.',\n invoke: (handle, args: { id: string }) => {\n handle.focusZone(args.id);\n },\n },\n get_zone: {\n safety: 'read',\n argsType: '{ id: string }',\n argsSchema: {\n type: 'object',\n properties: { id: ZONE_SCHEMA },\n required: ['id'],\n },\n returns: 'string[]',\n returnsSchema: { type: 'array', items: FINDING_SCHEMA },\n idempotent: true,\n descriptionKey: 'ui.agent.faceScheme.actions.getZone',\n description:\n 'Read the finding types recorded on one zone (notes are not returned).',\n invoke: (handle, args: { id: string }) =>\n handle.getZone(args.id).map((f) => f.type),\n },\n zones_with: {\n safety: 'read',\n argsType: '{ finding: FaceFindingType }',\n argsSchema: {\n type: 'object',\n properties: { finding: FINDING_SCHEMA },\n required: ['finding'],\n },\n returns: 'string[]',\n returnsSchema: { type: 'array', items: ZONE_SCHEMA },\n idempotent: true,\n descriptionKey: 'ui.agent.faceScheme.actions.zonesWith',\n description: 'List the zone ids carrying a given finding type.',\n invoke: (handle, args: { finding: FaceFindingType }) =>\n handle.zonesWith(args.finding),\n },\n set_finding: {\n safety: 'write',\n argsType: '{ id: string; finding: FaceFindingType; note?: string }',\n argsSchema: {\n type: 'object',\n properties: {\n id: ZONE_SCHEMA,\n finding: FINDING_SCHEMA,\n note: { type: 'string' },\n },\n required: ['id', 'finding'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.faceScheme.actions.setFinding',\n description:\n 'Record a finding on a zone, with an optional free-text note.',\n invoke: (\n handle,\n args: { id: string; finding: FaceFindingType; note?: string },\n ) => {\n handle.setFinding(args.id, args.finding, args.note);\n },\n },\n remove_finding: {\n safety: 'write',\n argsType: '{ id: string; finding: FaceFindingType }',\n argsSchema: {\n type: 'object',\n properties: { id: ZONE_SCHEMA, finding: FINDING_SCHEMA },\n required: ['id', 'finding'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.faceScheme.actions.removeFinding',\n description: 'Remove a single finding from a zone.',\n invoke: (handle, args: { id: string; finding: FaceFindingType }) => {\n handle.removeFinding(args.id, args.finding);\n },\n },\n clear_zone: {\n safety: 'destructive',\n argsType: '{ id: string }',\n argsSchema: {\n type: 'object',\n properties: { id: ZONE_SCHEMA },\n required: ['id'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.faceScheme.actions.clearZone',\n description: 'Clear every finding from a zone.',\n invoke: (handle, args: { id: string }) => {\n handle.clearZone(args.id);\n },\n },\n auto_align: {\n safety: 'write',\n argsType: '{}',\n argsSchema: { type: 'object', properties: {} },\n idempotent: true,\n descriptionKey: 'ui.agent.faceScheme.actions.autoAlign',\n description:\n 'Run the on-device detector to align the zone overlay to the patient photo (works on the active view — front or profile). No-op if detection is disabled or no face is found.',\n invoke: (handle) => {\n void handle.detect();\n },\n },\n set_view: {\n safety: 'write',\n argsType: '{ view: FaceViewId }',\n argsSchema: {\n type: 'object',\n properties: { view: VIEW_SCHEMA },\n required: ['view'],\n },\n idempotent: true,\n descriptionKey: 'ui.agent.faceScheme.actions.setView',\n description:\n \"Switch the active view ('front' / 'profile-left' / 'profile-right'). Each view renders a different zone set, so switch before focusing a zone that belongs to another view.\",\n invoke: (handle, args: { view: FaceViewId }) => {\n handle.setView(args.view);\n },\n },\n },\n domHooks: {\n root: { attr: 'data-component', value: 'face-scheme' },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n item: {\n attr: 'data-zone',\n description:\n 'Each clickable zone `<g>` carries its zone id as `data-zone` (plus `data-side` for the anatomical side). The root `<div>` carries the active view id as `data-view` — only the zones for the active view are in the DOM.',\n },\n },\n};\n","/* ------------------------------------------------------------------ */\n/* FaceScheme — on-device face-landmark detection (lazy). */\n/* */\n/* Wraps Google's MediaPipe Face Landmarker. The model + wasm runtime */\n/* are LAZY-LOADED from a CDN (overridable) only when detection actually */\n/* runs — so the kit's base bundle ships ZERO ML and FaceScheme stays */\n/* lightweight for consumers that never auto-detect. Everything runs */\n/* on-device: the patient photo and the derived landmarks never leave */\n/* the browser. The same helper is exported for consumer-side use. */\n/* */\n/* It returns canonical-anchor → photo-point pairs in the FaceScheme */\n/* view coordinate space, ready to hand to `FaceSchemeHandle.fitToLand- */\n/* marks` (or the kit feeds them internally for Auto-fit). */\n/* ------------------------------------------------------------------ */\n\nimport type { Point } from './face-scheme-data';\n\n/** Default CDN sources — override to self-host (recommended for production CSP). */\nconst DEFAULT_VISION_MODULE =\n 'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.18/vision_bundle.mjs';\nconst DEFAULT_WASM_BASE =\n 'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.18/wasm';\nconst DEFAULT_MODEL =\n 'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task';\n\n/**\n * Canonical FaceScheme anchor → MediaPipe FaceMesh landmark indices.\n * Multiple indices are averaged. Sides are anatomical (the subject's right eye\n * sits on the viewer's left), matching the canonical anchor ids in the data\n * module. Indices are the stable FaceMesh topology (468/478-point).\n */\nconst ANCHOR_INDICES: Record<string, number[]> = {\n 'right-eye': [33, 133], // subject's right eye, outer + inner corners\n 'left-eye': [263, 362], // subject's left eye, outer + inner corners\n 'nose-tip': [1],\n chin: [152],\n // Midline cephalometric points — the profile views fit on these (the detector\n // tracks them on a side profile even though the contralateral side is\n // occluded). The front view ignores ids it has no landmark for.\n nasion: [168], // bridge of the nose, between the brows\n subnasale: [2], // base of the nose / top of the philtrum\n};\n\nexport interface DetectFaceLandmarksOptions {\n /** View coordinate width the points must be projected into (the SVG viewBox W). */\n viewWidth: number;\n /** View coordinate height (the SVG viewBox H). */\n viewHeight: number;\n /** ESM bundle URL for `@mediapipe/tasks-vision`. */\n visionModuleUrl?: string;\n /** Base URL for the MediaPipe wasm fileset. */\n wasmBaseUrl?: string;\n /** URL of the `face_landmarker.task` model. */\n modelAssetPath?: string;\n /** Lifecycle hook — `'loading'` while the model initialises, `'detecting'` during inference. */\n onStage?: (stage: 'loading' | 'detecting') => void;\n}\n\ninterface NormalizedLandmark {\n x: number;\n y: number;\n z: number;\n}\ninterface FaceLandmarkerResult {\n faceLandmarks: NormalizedLandmark[][];\n}\ninterface FaceLandmarkerInstance {\n detect: (image: HTMLImageElement) => FaceLandmarkerResult;\n}\ninterface VisionModule {\n FilesetResolver: { forVisionTasks: (wasmBase: string) => Promise<unknown> };\n FaceLandmarker: {\n createFromOptions: (\n fileset: unknown,\n options: {\n baseOptions: { modelAssetPath: string; delegate?: 'CPU' | 'GPU' };\n runningMode: 'IMAGE' | 'VIDEO';\n numFaces: number;\n },\n ) => Promise<FaceLandmarkerInstance>;\n };\n}\n\n// One landmarker per unique source triple, loaded once and reused.\nconst landmarkerCache = new Map<string, Promise<FaceLandmarkerInstance>>();\n\nfunction getLandmarker(\n visionModuleUrl: string,\n wasmBaseUrl: string,\n modelAssetPath: string,\n onStage?: (stage: 'loading' | 'detecting') => void,\n): Promise<FaceLandmarkerInstance> {\n const key = `${visionModuleUrl}|${wasmBaseUrl}|${modelAssetPath}`;\n const cached = landmarkerCache.get(key);\n if (cached) return cached;\n onStage?.('loading');\n // Variable specifier + @vite-ignore: nothing is bundled or pre-resolved; the\n // ESM module is fetched at call time. Cast through `unknown` — no `any`.\n const promise = (async () => {\n const mod = (await import(\n /* @vite-ignore */ visionModuleUrl\n )) as unknown as VisionModule;\n const fileset = await mod.FilesetResolver.forVisionTasks(wasmBaseUrl);\n return mod.FaceLandmarker.createFromOptions(fileset, {\n baseOptions: { modelAssetPath, delegate: 'GPU' },\n runningMode: 'IMAGE',\n numFaces: 1,\n });\n })();\n landmarkerCache.set(key, promise);\n // Don't cache a rejected init — let the next call retry.\n promise.catch(() => landmarkerCache.delete(key));\n return promise;\n}\n\nfunction loadImage(src: string): Promise<HTMLImageElement> {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.src = src;\n return img.decode().then(() => img);\n}\n\n/**\n * Detect a single face in `src` and return the FaceScheme canonical-anchor →\n * photo-point pairs, projected into the view coordinate space using the same\n * `xMidYMid slice` (cover) fit the FaceScheme `<image>` uses. Resolves `null`\n * if no face is found. Browser-only.\n */\nexport async function detectFaceLandmarks(\n src: string,\n options: DetectFaceLandmarksOptions,\n): Promise<Array<{ id: string; photo: Point }> | null> {\n if (typeof window === 'undefined' || typeof Image === 'undefined') {\n throw new Error('detectFaceLandmarks runs in the browser only.');\n }\n const {\n viewWidth,\n viewHeight,\n visionModuleUrl = DEFAULT_VISION_MODULE,\n wasmBaseUrl = DEFAULT_WASM_BASE,\n modelAssetPath = DEFAULT_MODEL,\n onStage,\n } = options;\n\n const [image, landmarker] = await Promise.all([\n loadImage(src),\n getLandmarker(visionModuleUrl, wasmBaseUrl, modelAssetPath, onStage),\n ]);\n\n onStage?.('detecting');\n const result = landmarker.detect(image);\n const face = result.faceLandmarks[0];\n if (!face || face.length === 0) return null;\n\n // Replicate the component's cover (slice) placement: the image fills the\n // viewBox, centred, cropped — map normalized landmarks → view coordinates.\n const imgW = image.naturalWidth || image.width;\n const imgH = image.naturalHeight || image.height;\n const scale = Math.max(viewWidth / imgW, viewHeight / imgH);\n const offsetX = (viewWidth - imgW * scale) / 2;\n const offsetY = (viewHeight - imgH * scale) / 2;\n const toView = (lm: NormalizedLandmark): Point => ({\n x: offsetX + lm.x * imgW * scale,\n y: offsetY + lm.y * imgH * scale,\n });\n\n const pairs: Array<{ id: string; photo: Point }> = [];\n for (const [id, indices] of Object.entries(ANCHOR_INDICES)) {\n let sx = 0;\n let sy = 0;\n let count = 0;\n for (const idx of indices) {\n const lm = face[idx];\n if (!lm) continue;\n const v = toView(lm);\n sx += v.x;\n sy += v.y;\n count++;\n }\n if (count > 0) pairs.push({ id, photo: { x: sx / count, y: sy / count } });\n }\n return pairs.length >= 3 ? pairs : null;\n}\n","/* ------------------------------------------------------------------ */\n/* FaceScheme — on-device PROFILE face detection (lazy). */\n/* */\n/* The MediaPipe FaceMesh family (see detect-face-landmarks.ts) is */\n/* trained near-frontal: on a 90° lateral profile it under-reads the */\n/* pose and hallucinates the occluded half of the face, so it can't */\n/* register the profile mesh. YuNet (OpenCV Zoo) is a WIDERFACE-trained */\n/* CNN detector that tracks the VISIBLE-side keypoints across poses, so */\n/* the kit uses it for the two profile views. */\n/* */\n/* Runtime: onnxruntime-web is LAZY-LOADED from a CDN (overridable) and */\n/* the ~232 KB model is a code-split asset fetched only when profile */\n/* auto-fit actually runs — the base bundle ships zero ML, matching the */\n/* MediaPipe wrapper. Everything runs on-device: the patient photo and */\n/* the derived keypoints never leave the browser. */\n/* */\n/* It returns YuNet's five canonical keypoints (two eyes, nose tip, two */\n/* mouth corners) projected into the FaceScheme view coordinate space. */\n/* The component picks the visible-side eye + mouth for the profile fit. */\n/* ------------------------------------------------------------------ */\n\nimport type { Point } from './face-scheme-data';\n\n/** Default CDN sources — override to self-host (recommended for production CSP). */\nconst DEFAULT_ORT_MODULE =\n 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.19.2/dist/ort.min.mjs';\nconst DEFAULT_ORT_WASM_BASE =\n 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.19.2/dist/';\n\n/** YuNet 2023mar: fixed 640×640 input, decoded over three strides. */\nconst NET_SIZE = 640;\nconst STRIDES = [8, 16, 32] as const;\n/** YuNet keypoint order → FaceScheme anchor ids (sides are the subject's). */\nconst KEYPOINT_IDS = [\n 'right-eye',\n 'left-eye',\n 'nose-tip',\n 'mouth-right',\n 'mouth-left',\n] as const;\n\nexport interface DetectFaceYuNetOptions {\n /** View coordinate width the points must be projected into (the SVG viewBox W). */\n viewWidth: number;\n /** View coordinate height (the SVG viewBox H). */\n viewHeight: number;\n /** ESM bundle URL for `onnxruntime-web`. */\n ortModuleUrl?: string;\n /** Base URL for the ORT wasm fileset. */\n ortWasmBaseUrl?: string;\n /** URL of the YuNet `.onnx` model (defaults to the kit's bundled copy). */\n modelUrl?: string;\n /** Minimum detection score (default 0.6). */\n scoreThreshold?: number;\n /** Lifecycle hook — `'loading'` while the runtime/model initialise, `'detecting'` during inference. */\n onStage?: (stage: 'loading' | 'detecting') => void;\n}\n\ninterface OrtTensor {\n data: Float32Array;\n dims: readonly number[];\n}\ninterface OrtSession {\n inputNames: string[];\n run: (feeds: Record<string, OrtTensor>) => Promise<Record<string, OrtTensor>>;\n}\ninterface OrtModule {\n env: { wasm: { wasmPaths: string; numThreads: number }; logLevel?: string };\n Tensor: new (\n type: 'float32',\n data: Float32Array,\n dims: number[],\n ) => OrtTensor;\n InferenceSession: {\n create: (\n model: Uint8Array,\n options: { executionProviders: string[] },\n ) => Promise<OrtSession>;\n };\n}\n\n// One session per unique source pair, loaded once and reused.\nconst sessionCache = new Map<\n string,\n Promise<{ ort: OrtModule; session: OrtSession }>\n>();\n\nasync function getSession(\n ortModuleUrl: string,\n ortWasmBaseUrl: string,\n modelUrl: string,\n onStage?: (stage: 'loading' | 'detecting') => void,\n): Promise<{ ort: OrtModule; session: OrtSession }> {\n const key = `${ortModuleUrl}|${ortWasmBaseUrl}|${modelUrl}`;\n const cached = sessionCache.get(key);\n if (cached) return cached;\n onStage?.('loading');\n // Variable specifier + @vite-ignore: nothing is bundled or pre-resolved; the\n // ESM runtime is fetched at call time. Cast through `unknown` — no `any`.\n const promise = (async () => {\n const ort = (await import(\n /* @vite-ignore */ ortModuleUrl\n )) as unknown as OrtModule;\n ort.env.wasm.wasmPaths = ortWasmBaseUrl;\n // Single-threaded: avoids the cross-origin-isolation (COOP/COEP) requirement\n // that SharedArrayBuffer-backed threads impose on the host page.\n ort.env.wasm.numThreads = 1;\n ort.env.logLevel = 'error';\n const model = new Uint8Array(await (await fetch(modelUrl)).arrayBuffer());\n const session = await ort.InferenceSession.create(model, {\n executionProviders: ['wasm'],\n });\n return { ort, session };\n })();\n sessionCache.set(key, promise);\n promise.catch(() => sessionCache.delete(key));\n return promise;\n}\n\nfunction loadImage(src: string): Promise<HTMLImageElement> {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.src = src;\n return img.decode().then(() => img);\n}\n\n/** Resolve the kit's bundled model URL (code-split — fetched only on demand). */\nasync function defaultModelUrl(): Promise<string> {\n const mod = (await import('./face_detection_yunet_2023mar.onnx?url')) as {\n default: string;\n };\n return mod.default;\n}\n\ninterface Candidate {\n score: number;\n keypoints: Array<[number, number]>;\n}\n\n/** Letterbox into NET_SIZE², BGR, NCHW, 0–255 (YuNet/OpenCV convention). */\nfunction preprocess(\n Tensor: OrtModule['Tensor'],\n img: HTMLImageElement,\n): { tensor: OrtTensor; scale: number; padX: number; padY: number } {\n const w = img.naturalWidth || img.width;\n const h = img.naturalHeight || img.height;\n const scale = Math.min(NET_SIZE / w, NET_SIZE / h);\n const dw = Math.round(w * scale);\n const dh = Math.round(h * scale);\n const padX = Math.floor((NET_SIZE - dw) / 2);\n const padY = Math.floor((NET_SIZE - dh) / 2);\n const canvas = document.createElement('canvas');\n canvas.width = NET_SIZE;\n canvas.height = NET_SIZE;\n const ctx = canvas.getContext('2d');\n if (!ctx) throw new Error('2D canvas context unavailable.');\n // Letterbox padding is black: a fresh canvas's default fillStyle is already\n // opaque black, so fillRect needs no colour (YuNet expects zero-padding).\n ctx.fillRect(0, 0, NET_SIZE, NET_SIZE);\n ctx.drawImage(img, padX, padY, dw, dh);\n const { data } = ctx.getImageData(0, 0, NET_SIZE, NET_SIZE);\n const n = NET_SIZE * NET_SIZE;\n const f = new Float32Array(3 * n);\n for (let i = 0; i < n; i++) {\n f[i] = data[i * 4 + 2]; // B\n f[n + i] = data[i * 4 + 1]; // G\n f[2 * n + i] = data[i * 4]; // R\n }\n return {\n tensor: new Tensor('float32', f, [1, 3, NET_SIZE, NET_SIZE]),\n scale,\n padX,\n padY,\n };\n}\n\nconst sigmoid = (x: number): number => 1 / (1 + Math.exp(-x));\n\n/** Decode YuNet's 12 outputs (cls/obj/bbox/kps × 3 strides) → keypoint sets. */\nfunction decode(\n out: Record<string, OrtTensor>,\n scoreThreshold: number,\n): Candidate | null {\n // 2023mar bakes the sigmoid in; older exports emit logits — detect + apply.\n let needSigmoid = false;\n const probe = out['cls_8'].data;\n for (let i = 0; i < Math.min(probe.length, 256); i++) {\n if (probe[i] < 0 || probe[i] > 1) {\n needSigmoid = true;\n break;\n }\n }\n let best: Candidate | null = null;\n for (const s of STRIDES) {\n const cls = out[`cls_${s}`].data;\n const obj = out[`obj_${s}`].data;\n const kps = out[`kps_${s}`].data;\n const cols = NET_SIZE / s;\n const rows = NET_SIZE / s;\n const n = cols * rows;\n for (let i = 0; i < n; i++) {\n let cv = cls[i];\n let ov = obj[i];\n if (needSigmoid) {\n cv = sigmoid(cv);\n ov = sigmoid(ov);\n }\n cv = Math.min(Math.max(cv, 0), 1);\n ov = Math.min(Math.max(ov, 0), 1);\n const score = Math.sqrt(cv * ov);\n if (score < scoreThreshold) continue;\n if (best && score <= best.score) continue;\n const c = i % cols;\n const r = Math.floor(i / cols);\n const keypoints: Array<[number, number]> = [];\n for (let j = 0; j < 5; j++) {\n keypoints.push([\n (c + kps[i * 10 + 2 * j]) * s,\n (r + kps[i * 10 + 2 * j + 1]) * s,\n ]);\n }\n best = { score, keypoints };\n }\n }\n return best;\n}\n\n/**\n * Detect a single face in `src` and return YuNet's canonical keypoint →\n * photo-point pairs, projected into the view coordinate space using the same\n * `xMidYMid slice` (cover) fit the FaceScheme `<image>` uses. Resolves `null`\n * if no face is found. Browser-only.\n */\nexport async function detectFaceYuNet(\n src: string,\n options: DetectFaceYuNetOptions,\n): Promise<Array<{ id: string; photo: Point }> | null> {\n if (typeof window === 'undefined' || typeof Image === 'undefined') {\n throw new Error('detectFaceYuNet runs in the browser only.');\n }\n const {\n viewWidth,\n viewHeight,\n ortModuleUrl = DEFAULT_ORT_MODULE,\n ortWasmBaseUrl = DEFAULT_ORT_WASM_BASE,\n modelUrl = await defaultModelUrl(),\n scoreThreshold = 0.6,\n onStage,\n } = options;\n\n const [image, { ort, session }] = await Promise.all([\n loadImage(src),\n getSession(ortModuleUrl, ortWasmBaseUrl, modelUrl, onStage),\n ]);\n\n onStage?.('detecting');\n const { tensor, scale, padX, padY } = preprocess(ort.Tensor, image);\n const result = await session.run({ [session.inputNames[0]]: tensor });\n const face = decode(result, scoreThreshold);\n if (!face) return null;\n\n // Replicate the component's cover (slice) placement of the <image>.\n const imgW = image.naturalWidth || image.width;\n const imgH = image.naturalHeight || image.height;\n const coverScale = Math.max(viewWidth / imgW, viewHeight / imgH);\n const offsetX = (viewWidth - imgW * coverScale) / 2;\n const offsetY = (viewHeight - imgH * coverScale) / 2;\n\n return face.keypoints.map(([kx, ky], j) => {\n // Un-letterbox (network space → original image px), then project to view.\n const px = (kx - padX) / scale;\n const py = (ky - padY) / scale;\n return {\n id: KEYPOINT_IDS[j],\n photo: {\n x: offsetX + px * coverScale,\n y: offsetY + py * coverScale,\n },\n };\n });\n}\n","/* Provenance — FRONT zones are AUTO-GENERATED by scratchpad/gen-zones.mjs from\n platform faceElements.js and are byte-identical to the platform FaceMap.\n PROFILE zones diverge from the platform on purpose: the platform side-profile is a\n photo-less schematic with a stylised, over-deep skull that no rigid fit can register\n onto a real photographed profile. The kit therefore reshapes the platform-authored\n profile paths with a thin-plate-spline warp pinned to anatomical landmarks (crown,\n nasion, eye, nose-tip, subnasale, lips, chin, jaw-angle, ear, back-skull, nape) so the\n mesh traces a real face for the photo overlay. Do not hand-edit the path data: re-run\n the front generator, or the profile TPS bake (scratchpad/faces/tps + bake-zones), and\n paste the output here. Centroids are the path-bounds midpoint, used to anchor finding\n markers. viewBox is the platform 0 0 808 944. */\n\nimport type { FaceLandmarkSeed, FaceZoneSeed } from './face-scheme-data';\n\nexport const FRONT_VIEWBOX = '0 0 808 944';\nexport const FRONT_PIVOT = { x: 404, y: 472 } as const;\n\nexport const FRONT_CONTOUR: FaceZoneSeed = {\n id: 'faceContour',\n labelKey: 'faceContour',\n d: 'M688.38,439.4c-9.78,0-15.37,4.04-17.86,6.47,1.31-10.99,2.5-20.62,3.49-28.2,7.55-57.89,5.03-94.39,5.03-94.39-15.15-165.92-111.73-221.46-186.21-239.39h0s-.1-.02-.1-.02c-6.18-1.49-12.21-2.72-18.02-3.73l-.96-.2c-46.02-9.51-93.5-9.51-139.52,0l-.95.2c-5.82,1.01-11.85,2.24-18.04,3.73l-.08.02h0c-74.48,17.94-171.06,73.48-186.21,239.39,0,0-2.52,36.5,5.03,94.39,1,7.68,2.2,17.45,3.53,28.61-2.25-2.35-7.91-6.88-18.26-6.88-13.97,0-29.06,16.21-20.12,63.71,8.94,47.5,17.3,80.48,18.43,97.24,1.11,16.47,24.08,47.91,41.43,38.23.9,8.33,1.42,13.17,1.42,13.17,0,0,8.81,59.15,56.63,104.46,12.62,11.95,25.84,23.46,38.49,33.94,7.14-4.56,24-16.96,29.06-35.21.9-3.24,1.73-6.33,2.52-9.29,2.46-9.17,4.59-17.1,7.4-24.46,3.44-9.01,7.62-16.28,13.25-23.04-47.54-46.57-49.82-89.3-48.75-104.93-21.75-2.47-38.06-19.4-47.37-49.37-7.27-23.39-7.77-46.39-7.79-47.36-.02-1.34.43-2.57,1.21-3.54-.47-2.21-.65-3.58-.67-3.77-.28-2.23.97-4.38,3.05-5.24.34-.14.69-.23,1.04-.29.47-3.26,2.23-10.86,8.8-18.21,10.82-12.11,28.95-17.92,53.93-17.22,41.5,1.13,55.11,17.49,59.58,28.15,1.45.85,2.49,2.34,2.68,4.14,0,.05.02.21.05.45.22.48.36,1,.42,1.56.04.41.34,3.71.16,8.68.17,6.11.1,14.39-.75,23.68-.27,2.9-.6,5.73-.98,8.49,4.1-9.94,7.68-20.29,11.15-30.32l.31-.89-10.84-54.18c-2.15-3.39-8.76-12.5-21.75-19.51-21.63-11.68-51.16-12.3-85.4-1.8-2.58.79-5.32-.61-6.19-3.16l-53.03-154.45c-.88-1.09-1.31-2.52-1.05-4.01.26-1.43,6.68-35.35,39.46-69.52,19.15-19.96,43.37-35.84,72.01-47.21,35.3-14.02,77.45-21.18,125.28-21.27h0s.98,0,.98,0c47.83.1,89.98,7.25,125.28,21.27,28.63,11.37,52.86,27.26,72.01,47.21,30.62,31.92,38.24,63.61,39.32,68.82.29.8.42,1.35.47,1.59.17.87.11,1.77-.17,2.61l-53.21,154.96c-.88,2.55-3.62,3.95-6.19,3.16-34.24-10.5-63.77-9.88-85.4,1.8-11.42,6.17-17.92,13.96-20.78,18.06l-11.25,56.25.04.11c2.95,8.52,5.98,17.28,9.34,25.81-.14-1.26-.28-2.53-.4-3.82-.53-5.79-.75-11.19-.81-15.92-1.11-9.09-.51-15.85-.45-16.44.11-1.1.59-2.08,1.3-2.85.19-.72.52-1.37.96-1.94,3.86-10.54,16.77-28.3,60.04-29.48,24.93-.69,43.04,5.1,53.85,17.2,6.53,7.3,8.3,14.85,8.78,18.13.53.04,1.05.16,1.56.37,2.08.86,3.32,3.01,3.05,5.24-.02.14-.12.93-.37,2.25,1.7.96,2.84,2.8,2.8,4.89-.02.97-.52,23.97-7.79,47.36-8.99,28.91-24.49,45.69-45.11,49.05,1.16,15.29-.68,58.78-49.42,106.1,5.35,6.56,9.36,13.63,12.68,22.33,2.81,7.36,4.94,15.28,7.4,24.45.79,2.96,1.62,6.05,2.52,9.29,5.07,18.28,21.97,30.69,29.09,35.23,12.65-10.48,25.88-21.99,38.5-33.95,47.82-45.31,56.63-104.46,56.63-104.46,0,0,.51-4.77,1.4-13,17.31,9.1,39.99-22.04,41.09-38.41,1.13-16.77,9.49-49.74,18.43-97.24,8.94-47.5-6.15-63.71-20.12-63.71Z',\n};\n\nexport const FRONT_ZONES: FaceZoneSeed[] = [\n {\n id: 'lateralNeckRight',\n labelKey: 'lateralNeckRight',\n d: 'M234.37,783.6s32.84,133.08,163.93,137.04v-48.41s-103.52,3.15-163.93-88.63Z',\n cx: 316,\n cy: 852,\n side: 'right',\n },\n {\n id: 'lateralNeckLeft',\n labelKey: 'lateralNeckLeft',\n d: 'M572.98,783.6s-32.84,133.08-163.93,137.04v-48.41s103.52,3.15,163.93-88.63Z',\n cx: 491,\n cy: 852,\n side: 'left',\n },\n {\n id: 'forehead',\n labelKey: 'forehead',\n d: 'M404,206.87s-.1,0-.14,0h-1.06s-.09,0-.14,0c-193.61.2-222.63,42.82-226.18,50.14l51.08,148.75c44.34-12.51,72.83-4.7,89.09,4.28,11.9,6.57,19.12,14.63,22.86,19.75,9.12-6.42,21.26-11.05,36.17-13.79,13.2-2.43,24.31-2.56,27.4-2.54.05,0,.1,0,.16,0h.04s.07,0,.11,0h.04c.2,0,.4,0,.64,0,3.65,0,14.33.22,26.92,2.54,14.91,2.74,27.05,7.37,36.17,13.79,3.74-5.12,10.96-13.18,22.86-19.75,16.26-8.98,44.75-16.79,89.09-4.28l51.08-148.75c-3.54-7.32-32.57-49.94-226.18-50.14Z',\n cx: 403,\n cy: 318,\n side: 'midline',\n },\n {\n id: 'glabella',\n labelKey: 'glabella',\n d: 'M403.28,472.75s.03,0,.04,0c.11,0,.22,0,.34,0,.11,0,.22,0,.34,0,.02,0,.03,0,.04,0,28.06.09,43.26,5.62,51.09,10.26l8.65-43.23c-19.58-15.79-54.78-16.31-59.89-16.33-.13,0-.27,0-.4,0h-.16s-.16,0-.16,0c-.13,0-.27,0-.4,0-5.09-.02-39.63.51-59.32,15.85l8.74,43.71c7.83-4.64,23.03-10.17,51.09-10.26Z',\n cx: 404,\n cy: 453,\n side: 'midline',\n },\n {\n id: 'nasalBase',\n labelKey: 'nasalBase',\n d: 'M403.9,608.63c-.19.02-.38.06-.58.06-34.25,0-47.76,9.72-52.65,15.19-.02.21-.05.42-.1.62,10.07,8.84,28.11,13.48,52.75,13.48.15,0,.29.03.43.04.15-.01.29-.04.43-.04,24.98,0,43.18-4.76,53.17-13.83-4.69-5.41-18.09-15.46-52.89-15.46-.2,0-.38-.04-.58-.06Z',\n cx: 404,\n cy: 623,\n side: 'midline',\n },\n {\n id: 'nasolabialFold',\n labelKey: 'nasolabialFold',\n d: 'M347.77,580.98c-2.28,3.78-4.09,6.76-5.04,9.96-1.38,4.58-3.68,15.46,1.36,25.35,7.25-7.51,23.63-17.61,59.24-17.61.2,0,.38.04.58.06.19-.02.38-.06.58-.06,35.31,0,51.71,9.93,59.05,17.42,4.92-9.84,2.63-20.61,1.27-25.17-.96-3.19-2.76-6.18-5.04-9.96-5.23-8.65-12.39-20.5-14.97-45.03-2.34-22.26,2.15-36.18,6.11-43.75-5.65-3.61-19.1-9.4-47.23-9.45-27.9.05-41.36,5.75-47.09,9.36,3.98,7.55,8.51,21.49,6.16,43.84-2.58,24.53-9.74,36.38-14.97,45.03Z',\n cx: 404,\n cy: 550,\n side: 'midline',\n },\n {\n id: 'medialCheekboneRight',\n labelKey: 'medialCheekboneRight',\n d: 'M310.99,528.29c6.75-7.41,10.08-16.87,11.7-25.19-.55.67-1.13,1.34-1.73,2.02-11.93,13.41-27.69,20.63-45.58,20.88-24.57.33-43.87-10.86-55.92-20.57,6.83,15.71,22.29,36.28,56.41,36.28,15.2,0,27.02-4.51,35.12-13.42Z',\n cx: 271,\n cy: 522,\n side: 'right',\n },\n {\n id: 'medialCheekboneLeft',\n labelKey: 'medialCheekboneLeft',\n d: 'M529.65,526c-17.89-.25-33.66-7.47-45.58-20.88-.48-.54-.94-1.08-1.39-1.62.02.49.04.99.06,1.49,1.74,7.84,5.06,16.45,11.3,23.3,8.11,8.9,19.92,13.42,35.12,13.42,34.11,0,49.57-20.57,56.41-36.28-12.05,9.71-31.35,20.9-55.92,20.57Z',\n cx: 534,\n cy: 523,\n side: 'left',\n },\n {\n id: 'lateralCheekLeft',\n labelKey: 'lateralCheekLeft',\n d: 'M540.15,593.37c-18.18-.45-32.31-6.73-43.63-16.5-1.18-.24-2.29-.88-3.12-1.88-1.11-1.34-2.16-2.75-3.18-4.2-15.93-17.11-25.66-41.5-33.74-64.38-1.86,6.64-3.07,15.9-1.75,28.49,2.35,22.31,8.58,32.62,13.58,40.91,2.44,4.04,4.74,7.85,6.06,12.26,1.9,6.35,5.16,22.19-4,36.16,7.6,9.08,23.47,31.53,26.94,63.94,25.78-26.28,36.07-50.77,40.13-67.19,3.11-12.6,3.1-22.36,2.72-27.61Z',\n cx: 497,\n cy: 597,\n side: 'left',\n },\n {\n id: 'lateralCheekRight',\n labelKey: 'lateralCheekRight',\n d: 'M350.8,505.59c-14.12,40.11-33.1,85.3-81.73,87.86-.37,5.26-.37,15,2.72,27.53,4.02,16.29,14.18,40.52,39.5,66.55,3.52-31.28,18.56-53.18,26.31-62.61-9.73-14.13-6.4-30.4-4.46-36.85,1.32-4.41,3.62-8.22,6.06-12.26,5-8.29,11.23-18.6,13.58-40.91,1.38-13.12,0-22.63-1.99-29.32Z',\n cx: 311,\n cy: 597,\n side: 'right',\n },\n {\n id: 'zygomaticArchRight',\n labelKey: 'zygomaticArchRight',\n d: 'M319.21,534.06c-.26.3-.51.61-.78.91-10.08,11.1-24.4,16.73-42.56,16.73-30.94,0-48.47-15.12-58-28.84,4.69,25.35,16.61,59.69,46.82,59.69,16.21,0,28.81-5.46,38.96-14.31.06-.09.11-.18.18-.26,7.83-9.48,12.57-21.69,15.39-33.93Z',\n cx: 269,\n cy: 553,\n side: 'right',\n },\n {\n id: 'zygomaticArchLeft',\n labelKey: 'zygomaticArchLeft',\n d: 'M542.24,582.4c31.74,0,43.29-37.91,47.48-63.47-.28.46-.57.93-.86,1.4-9.09,14.32-26.82,31.38-59.69,31.38-18.08,0-32.36-5.59-42.43-16.6,2.41,10.07,6.15,20.06,11.85,28.48,10.9,11.46,24.85,18.81,43.65,18.81Z',\n cx: 538,\n cy: 551,\n side: 'left',\n },\n {\n id: 'upperForehead',\n labelKey: 'upperForehead',\n d: 'M524.46,147.46c-34.16-13.49-75.08-20.35-121.64-20.4-46.56.05-87.49,6.91-121.64,20.4-27.26,10.76-50.3,25.76-68.48,44.56-15.39,15.91-24.67,31.94-30.13,44.09,4.92-3.6,11.3-7.4,19.61-11.17,40.86-18.52,108.25-27.96,200.32-28.07.05,0,.11,0,.16,0h.68s.68,0,.68,0c.05,0,.11,0,.16,0,92.07.1,159.46,9.55,200.32,28.07,7.48,3.39,13.39,6.81,18.08,10.08-5.52-12-14.69-27.55-29.63-43-18.18-18.8-41.22-33.79-68.48-44.56Z',\n cx: 403,\n cy: 182,\n side: 'midline',\n },\n {\n id: 'marionetteLines',\n labelKey: 'marionetteLines',\n d: 'M344.66,676.82c23.78-8.45,44.18-6.91,59.31,4.4,15.13-11.32,35.54-12.87,59.33-4.41,9.61,3.41,17.99,7.81,23.93,11.32-3.32-27.6-16.56-47.44-23.55-56.2-11.99,10.66-31.94,16.05-59.49,16.05-.15,0-.29-.03-.43-.04-.15.01-.29.04-.43.04-27.09,0-46.83-5.21-58.88-15.52-7.05,8.98-19.76,28.44-23.08,55.31,5.9-3.44,14.03-7.65,23.29-10.94Z',\n cx: 404,\n cy: 660,\n side: 'midline',\n },\n {\n id: 'lowerLip',\n labelKey: 'lowerLip',\n d: 'M487.84,709.63c-4.92,5.03-11.64,11.3-19.65,17.27-17.67,13.18-35.13,19.92-51.28,19.92-4.52,0-8.94-.53-13.23-1.6-4.29,1.06-8.71,1.6-13.23,1.6-16.15,0-33.61-6.74-51.28-19.92-7.36-5.49-13.63-11.23-18.42-16.03,1.76,38.11,15.57,71.15,82.56,71.15.33,0,.64.04.95.1.31-.06.62-.1.95-.1,67.72,0,81.09-33.76,82.62-72.39Z',\n cx: 404,\n cy: 746,\n side: 'midline',\n },\n {\n id: 'lipCommissures',\n labelKey: 'lipCommissures',\n d: 'M407.45,691.27c-.9.8-2.02,1.19-3.13,1.23-1.34.13-2.72-.26-3.8-1.22-22.23-19.67-55.63-5.3-72.53,4.24,11.5,1.38,25.87,2.57,31.61,1.13,6.72-1.68,12.32.35,18.25,2.5,6.08,2.2,13.64,4.94,25.48,4.98,11.85-.04,19.41-2.78,25.48-4.98,5.93-2.15,11.53-4.18,18.25-2.5,5.93,1.48,21.03.17,32.69-1.26-16.96-9.54-50.17-23.72-72.31-4.13Z',\n cx: 404,\n cy: 688,\n side: 'midline',\n },\n {\n id: 'upperLip',\n labelKey: 'upperLip',\n d: 'M444.65,706.36c-3.59-.9-6.73.14-12.42,2.2-6.4,2.32-15.13,5.47-28.52,5.57-.03,0-.06,0-.09,0-.09,0-.18,0-.28,0-.09,0-.18,0-.28,0-.03,0-.06,0-.09,0-13.39-.1-22.12-3.25-28.52-5.57-5.69-2.06-8.83-3.1-12.42-2.2-6.87,1.72-20.85.72-32.07-.53,15.47,14.96,44.16,37.13,72.34,29.42.46-.13.93-.16,1.39-.15.46,0,.93.03,1.4.15,28.22,7.72,56.98-14.55,72.43-29.5-11.35,1.29-25.82,2.37-32.86.61Z',\n cx: 404,\n cy: 724,\n side: 'midline',\n },\n {\n id: 'submental',\n labelKey: 'submental',\n d: 'M402.47,796.81c-31.67.12-55.93,8.97-72.11,26.31-5.47,5.87-9.06,11.79-11.35,16.53.85.63,1.68,1.27,2.5,1.95,4.99,4.16,26.68,19.07,81.31,19.07s76.32-14.91,81.31-19.07c.65-.54,1.31-1.06,1.98-1.57-2.28-4.8-5.91-10.88-11.53-16.91-16.17-17.35-40.44-26.19-72.11-26.31Z',\n cx: 403,\n cy: 829,\n side: 'midline',\n },\n {\n id: 'mandibularAngleLeft',\n labelKey: 'mandibularAngleLeft',\n d: 'M514.03,757.42c-.91-3.27-1.74-6.39-2.54-9.37-2.39-8.91-4.46-16.6-7.08-23.48-1.84-4.83-3.93-9.07-6.37-12.99-1.26,23.6-6.85,40.74-17.31,53.43-10.17,12.33-24.87,20.32-44.53,24.16,17.13,3.95,34.7,11.85,48.39,26.79,5.59,6.1,9.46,12.22,12.09,17.4,10.43-7.53,28.1-20.6,47.98-36.72-9.19-6.29-25.19-19.64-30.61-39.21Z',\n cx: 490,\n cy: 772,\n side: 'left',\n },\n {\n id: 'mandibularAngleRight',\n labelKey: 'mandibularAngleRight',\n d: 'M372.3,789.03c-19.33-3.89-33.81-11.84-43.86-24.02-10.59-12.83-16.18-30.24-17.35-54.26-2.69,4.15-4.95,8.66-6.92,13.84-2.63,6.88-4.69,14.57-7.08,23.48-.8,2.98-1.64,6.1-2.54,9.37-5.42,19.55-21.39,32.89-30.58,39.19,19.56,15.86,36.98,28.78,47.47,36.36,2.63-5.1,6.44-11.07,11.9-17.03,13.85-15.11,31.67-23.02,48.98-26.92Z',\n cx: 318,\n cy: 772,\n side: 'right',\n },\n {\n id: 'eyelidRight',\n labelKey: 'eyelidRight',\n d: 'M270.93,458.23c-50.38-1.36-52.57,26.33-52.64,27.51-.08,1.39-.72,2.61-1.69,3.46,7.87,8.21,29.3,27.23,58.63,26.79,29.72-.41,43.34-22.6,47.54-31.28-.03-.39-.05-.75-.07-1.07-1.34-6.07-8.94-24.24-51.76-25.41Z',\n cx: 270,\n cy: 487,\n side: 'right',\n },\n {\n id: 'eyelidLeft',\n labelKey: 'eyelidLeft',\n d: 'M586.21,485.71c-.08-1.27-2.3-28.82-52.55-27.45-36.35.99-47.27,14.25-50.55,21.95.08.48.1.98.05,1.49-.01.11-.16,1.63-.31,4.22,4.82,9.37,18.52,29.69,46.94,30.08,29.04.43,50.34-18.21,58.4-26.55-1.13-.87-1.91-2.2-1.98-3.73Z',\n cx: 536,\n cy: 487,\n side: 'left',\n },\n];\n\nexport const FRONT_LANDMARKS: FaceLandmarkSeed[] = [\n { id: 'right-eye', labelKey: 'rightEye', x: 268, y: 487 },\n { id: 'left-eye', labelKey: 'leftEye', x: 535, y: 487 },\n { id: 'nose-tip', labelKey: 'noseTip', x: 403, y: 620 },\n { id: 'chin', labelKey: 'chin', x: 404, y: 858 },\n];\n\n/* Side-profile zone paths (left-facing), kept for a future profile view. Not yet\n wired into FACE_VIEWS — front is the photo-mapping surface today. */\nexport const PROFILE_ZONE_PATHS: FaceZoneSeed[] = [\n {\n id: 'cheekbone',\n labelKey: 'cheekbone',\n d: 'M610.94 390.75C608.16 382.3 605.46 381.87 596.6 382.08C591.51 382.28 584.59 382.59 575.62 381.39C557.25 379.38 481.3 349.58 454.86 341.75C422 333.52 392.16 327.22 375.57 324.41C381.84 329.92 388.57 338.91 380.64 346.05C389.64 348.16 401.43 351.27 415.06 355.22C467.31 373.66 535.67 406.59 557.94 417.54C568.72 423.72 574.39 429.66 578.65 434.5C584.76 442.1 588.17 446.76 600.61 448.17C605.08 448.79 608.5 446.59 611.06 441.43C614.87 433.1 616.26 409.71 610.94 390.76Z',\n cx: 496,\n cy: 387,\n },\n {\n id: 'profileContour',\n labelKey: 'profileContour',\n d: 'M679.49 460.92C662.93 432.48 622.82 379.58 610.17 357.37C607.54 340.28 611.01 332.13 582.48 277.31C541.29 213.09 467.7 142.01 335.51 118.16C214.35 137.81 168.28 242.2 178.26 335.41C163.99 397.4 203.96 424.45 203.96 424.45L235.52 453C242.29 459.8 254.86 462.31 264.65 458.66L321.41 442.2C328.49 440.1 326.07 430.35 318.89 425.44C297.28 411.69 267.1 389.83 273 362.76C276.61 346.81 288 334.19 300.84 324.75C319.85 310.82 349.78 307.41 366.39 317.62C366.54 317.73 366.64 317.84 366.72 317.96C372.18 318.69 388.18 321.17 409.63 325.09C405.81 321.88 402.77 318.08 400.57 313.75C397.08 307.29 396.4 301.59 396.28 300.57C389.27 281.5 410.62 239.48 419.79 210.81L419.86 210.53C420.33 208.66 422.64 207.79 425.9 208.29C429.16 208.82 432.81 210.6 435.45 212.93L544.85 305.25C548.57 308.5 549.65 311.64 547.27 312.72C545.88 313.32 544.21 314.81 542.63 316.93C542.95 317.13 543.14 317.25 543.14 317.25L578.22 341.99C579.74 339.92 581.98 337.32 585.35 335.66C588.85 333.29 593.45 333.53 602.9 346.57C603.48 347.63 604.8 349.91 605.69 351.36C606.35 352.44 606.45 352.51 606.01 351.68C605.24 350.27 604.53 349 603.85 347.85C608.9 356.45 620.49 381.67 616.12 376.62C609.85 371.55 601.15 349.63 596.39 340.46C594.58 340.08 592.85 340.4 591.22 341.48C588.15 343.06 586.14 345.68 584.84 347.55L587.8 350.22C588.64 351.01 589.24 351.93 589.46 352.78C589.67 353.64 589.47 354.34 588.93 354.73L561.7 370.22C569.18 372.95 573.07 373.93 575.12 374.17C583.51 375.24 589.9 374.95 595.07 374.74C603.66 374.53 609.22 374.57 614.07 388.6C618.2 402.65 621.13 431.87 614.87 446.84C611.24 454.21 606 457.66 599.76 456.78C596.46 456.37 593.65 455.7 591.21 454.82C587.73 453.61 585.02 451.95 582.66 449.97C582.53 453.68 582.22 458.38 581.59 463.89C587.14 465.39 598.05 470.35 603.05 485.32C605.84 495.29 605.31 507.77 601.29 522.54C603.25 526.51 609.11 537.5 620.13 538.58C628.34 539.19 636.2 543.59 641.84 549.94C646.74 545.1 652.14 538.43 654.44 531.02C655.29 518.93 652.79 509.96 656.5 508.65C659.72 507.91 672 512.24 681.12 500.96C689.12 489.32 690.1 489.92 679.53 460.96Z',\n cx: 427,\n cy: 334,\n },\n {\n id: 'temple',\n labelKey: 'temple',\n d: 'M532.36 315.34C532.57 314.85 532.88 314.51 533.27 314.29C535.46 311.03 538.01 308.57 540.36 307.57C540.87 307.35 540.68 306.7 539.9 306.02L430.37 214.29C429.7 213.7 429 213.52 428.64 213.47C428.29 213.41 427.67 213.39 427.56 213.86L427.48 214.14C425.45 221.87 420.09 238.28 415.47 255.43C409.74 269.9 401.22 289.46 405.13 299.8L405.18 299.95L405.19 300.1C405.22 300.32 406.48 322.76 432.19 329.92C432.5 330.01 432.79 330.12 433.05 330.25C441.15 332.22 449.62 334.37 458.27 336.64C495.23 347.63 521.06 355.99 538.7 361.94C536.62 359.47 534.53 356.66 532.75 353.55C528.88 347.24 527.33 341.48 527.2 336.3C525.08 326.23 528.9 321.14 531.31 317.29C531.35 317.24 531.37 317.19 531.4 317.14C531.76 316.51 532.09 315.92 532.35 315.34Z',\n cx: 471,\n cy: 288,\n },\n {\n id: 'preauricularArea',\n labelKey: 'preauricularArea',\n d: 'M404.06 424.3C405.42 426.58 404.49 428.64 401.99 428.89L430.02 475.88C440.29 480.63 455.17 488.9 470.83 502.17C477.68 508.4 484.74 516.12 491.29 525.49C498.26 511.18 499.64 482.61 494.41 443.27C491.07 415.76 484.68 392.64 483.22 387.83C458.42 377.47 432.33 367.56 410.47 360.25C398.03 356.52 387.16 353.53 378.59 351.41C378.25 356.06 379.25 364.97 384.91 382.96C391.82 402.36 403.92 424.06 404.06 424.29Z',\n cx: 439,\n cy: 438,\n },\n {\n id: 'mandibularBody',\n labelKey: 'mandibularBody',\n d: 'M467.95 509.13C456.53 499.21 445.4 492.21 436.52 487.47L459.05 533.53L459.14 533.79C459.89 536.18 467.05 571.24 498.43 592.3C500.51 593.92 502.72 595.62 505 597.38C503.47 562.75 491.22 532.29 467.95 509.13Z',\n cx: 471,\n cy: 542,\n },\n {\n id: 'submentalProfile',\n labelKey: 'submentalProfile',\n d: 'M561.28 637.02C560.29 636.32 559.31 635.56 558.35 634.74C550.86 628.63 541.7 615.89 529.55 597.01C529.52 596.98 529.5 596.94 529.47 596.9C529.09 596.39 528.71 595.89 528.32 595.37C520.33 585.52 512.8 576.33 508.52 571.48C509.7 580.62 510.18 590.53 509.86 601.23C532.11 622.53 558.17 652.04 558.17 652.04C558.17 652.04 579.6 677.67 591.07 642.74C587.26 643.92 583.05 644.26 578.83 643.73C572.73 643.21 566.77 640.97 561.27 637.03Z',\n cx: 550,\n cy: 625,\n },\n {\n id: 'masseter',\n labelKey: 'masseter',\n d: 'M573.89 440.59C569.65 435.8 564.53 430.44 554.61 424.7C543.46 418.85 521.77 406.59 494.84 393.1C500.65 416.63 515.03 501.76 496.1 533.47C500.2 541.35 503.71 550.24 506.34 560.17C507 560.02 507.7 560.24 508.29 560.88C508.51 561.11 517.6 571.65 528.38 584.23C550.75 552.98 563.86 516.16 570.4 490.42C576.1 466.55 576.18 448.24 575.95 443.02C575.27 442.23 574.59 441.42 573.88 440.59Z',\n cx: 536,\n cy: 489,\n },\n {\n id: 'cheek',\n labelKey: 'cheek',\n d: 'M531.91 588.63L531.97 589.2C534.27 588.94 543.9 587.63 554.51 581.36C570.49 572.8 583.41 557.03 592.56 533.24C599.01 515.51 600.78 501.14 597.95 490.55C594.05 478.42 585.01 474.09 580.29 472.68C579.27 478.6 577.89 485.15 576.05 492.18C568.74 518.83 554.61 556.58 531.91 588.63Z',\n cx: 566,\n cy: 531,\n },\n {\n id: 'chin',\n labelKey: 'chin',\n d: 'M633.63 557.45C629.08 552.63 623.13 549.04 616.46 548.35C606.36 547.06 600.75 538.23 597.86 532.69C597.5 533.64 597.15 534.59 596.76 535.56C585.82 562.54 571.02 580.56 554.12 589.53C547.61 593.34 541.62 595.3 537.29 596.35C545.21 608.73 553.26 621.16 560.49 627.26C573.24 638.7 586.3 638.16 594.18 633.77C594.79 631.8 595.4 629.7 596 627.43L605.24 597.03L605.32 596.82C605.67 595.83 608.76 587.94 612.99 586.93C617.67 585.64 629.4 566.81 632.93 558.71C633.12 558.27 633.35 557.86 633.61 557.47Z',\n cx: 585,\n cy: 586,\n },\n {\n id: 'browTail',\n labelKey: 'browTail',\n d: 'M540.86 320.68C540.86 320.68 520.89 338.47 552.84 365.63L580.98 350.28L540.86 320.68Z',\n cx: 551,\n cy: 343,\n },\n];\n","/* ------------------------------------------------------------------ */\n/* FaceScheme — data module (pure, no React). */\n/* */\n/* The clinical face-zone taxonomy (aesthetic-medicine front view), */\n/* the finding vocabulary + its shared marker glyphs, and the affine */\n/* registration maths used to map the canonical zone mesh onto a real */\n/* patient photo. This module is the swap seam: the FRONT zone `d` paths */\n/* are byte-identical to the platform FaceMap; the PROFILE paths are */\n/* TPS-reshaped to trace a real face (see face-scheme-zones.ts). */\n/* */\n/* Registration is a 6-DOF affine — independent horizontal + vertical */\n/* scale, rotation, shear and translation — emitted as an SVG */\n/* `transform` matrix on the mesh group (never an inline style). The */\n/* same `solveAffine` least-squares fit serves manual landmark drag */\n/* AND a consumer-/kit-run face detector (detect-face-landmarks.ts). */\n/* ------------------------------------------------------------------ */\n\nimport type { MarkerShape, MarkerTone } from '../annotation-marker';\nimport {\n FRONT_CONTOUR,\n FRONT_LANDMARKS,\n FRONT_PIVOT,\n FRONT_VIEWBOX,\n FRONT_ZONES,\n PROFILE_ZONE_PATHS,\n} from './face-scheme-zones';\n\n/* ------------------------------------------------------------------ */\n/* Geometry vocabulary */\n/* ------------------------------------------------------------------ */\n\nexport type FaceViewId = 'front' | 'profile-left' | 'profile-right';\nexport type FaceSide = 'left' | 'right' | 'midline';\n\nexport interface Point {\n x: number;\n y: number;\n}\n\n/** Raw zone shape emitted by the generator (cx/cy/side present for clickable\n * zones, absent on the contour guide). */\nexport interface FaceZoneSeed {\n id: string;\n labelKey: string;\n d: string;\n cx?: number;\n cy?: number;\n side?: FaceSide;\n}\nexport interface FaceLandmarkSeed {\n id: string;\n labelKey: string;\n x: number;\n y: number;\n}\n\n/** A clickable face zone — a region of the canonical mesh. */\nexport interface FaceZone {\n id: string;\n /** `faceScheme.zone.<labelKey>` translation key. */\n labelKey: string;\n /** SVG path data in the view's coordinate space. */\n d: string;\n /** Path-bounds centroid — anchors the finding marker. */\n cx: number;\n cy: number;\n /** Patient-anatomical side (not reading direction). */\n side: FaceSide;\n}\n\n/** A canonical landmark anchor used to fit the mesh to a photo. */\nexport interface FaceLandmark {\n id: string;\n /** `faceScheme.landmark.<labelKey>` translation key. */\n labelKey: string;\n x: number;\n y: number;\n}\n\nexport interface FaceView {\n id: FaceViewId;\n /** `faceScheme.view.<labelKey>` translation key. */\n labelKey: string;\n viewBox: string;\n /** Pivot for the registration controls (the face centre). */\n pivot: Point;\n /** The face outline, drawn as a non-interactive guide. */\n contour: { id: string; labelKey: string; d: string };\n zones: FaceZone[];\n /** Canonical anchors for landmark fitting / auto-detect. */\n landmarks: FaceLandmark[];\n /** Render the mesh horizontally mirrored (right profile = mirrored left). */\n mirror?: boolean;\n}\n\n// The side-profile contour is one of the profile seeds; the rest are the\n// clickable profile zones. They carry no `side` (a profile is single-sided),\n// so default it to 'midline' for the data-side hook.\nconst PROFILE_CONTOUR =\n PROFILE_ZONE_PATHS.find((z) => z.id === 'profileContour') ?? FRONT_CONTOUR;\nconst PROFILE_ZONES: FaceZone[] = PROFILE_ZONE_PATHS.filter(\n (z) => z.id !== 'profileContour',\n).map((z) => ({\n id: z.id,\n labelKey: z.labelKey,\n d: z.d,\n cx: z.cx ?? 0,\n cy: z.cy ?? 0,\n side: z.side ?? 'midline',\n}));\n\n// Canonical profile anchors on the reshaped side-profile mesh (the face faces +x).\n// A non-collinear EYE / NOSE-TIP / MOUTH triangle — exactly the visible-side\n// keypoints the YuNet profile detector returns, which (unlike the old collinear\n// nose-tip/nasion/subnasale/chin midline points) give the affine solver a\n// well-conditioned, shear-free fit. Because the profile mesh is TPS-reshaped to\n// sit on the canonical demo face, these coordinates are the detector's keypoints\n// *on that face* — so Auto-detect resolves to ~identity and preserves the fit\n// rather than re-distorting it. They double as the manual drag handles; the right\n// profile reuses them and mirrors the rendered result. See detect-face-yunet.ts.\nconst PROFILE_LANDMARKS: FaceLandmark[] = [\n { id: 'eye', labelKey: 'eye', x: 561, y: 374 },\n { id: 'nose-tip', labelKey: 'noseTip', x: 668, y: 432 },\n { id: 'mouth', labelKey: 'mouth', x: 592, y: 532 },\n];\n\nexport const FACE_VIEWS: Record<FaceViewId, FaceView> = {\n front: {\n id: 'front',\n labelKey: 'front',\n viewBox: FRONT_VIEWBOX,\n pivot: FRONT_PIVOT,\n contour: FRONT_CONTOUR,\n // The generator guarantees every clickable zone carries cx/cy/side.\n zones: FRONT_ZONES as FaceZone[],\n landmarks: FRONT_LANDMARKS,\n },\n // The reshaped profile zones face +x; the right profile is the same mesh\n // mirrored. Both fit on the eye/nose-tip/mouth anchors — auto-detect and manual\n // drag/sliders all work; the right profile mirrors the fit at render time.\n 'profile-left': {\n id: 'profile-left',\n labelKey: 'profileLeft',\n viewBox: FRONT_VIEWBOX,\n pivot: FRONT_PIVOT,\n contour: PROFILE_CONTOUR,\n zones: PROFILE_ZONES,\n landmarks: PROFILE_LANDMARKS,\n },\n 'profile-right': {\n id: 'profile-right',\n labelKey: 'profileRight',\n viewBox: FRONT_VIEWBOX,\n pivot: FRONT_PIVOT,\n contour: PROFILE_CONTOUR,\n zones: PROFILE_ZONES,\n landmarks: PROFILE_LANDMARKS,\n mirror: true,\n },\n};\n\nexport const FACE_VIEW_IDS: FaceViewId[] = [\n 'front',\n 'profile-left',\n 'profile-right',\n];\n\n/* ------------------------------------------------------------------ */\n/* Finding vocabulary — generic clinical annotations. */\n/* */\n/* Deliberately practice-neutral: the consumer maps these to its own */\n/* clinical meaning. The SHAPE is the accessible channel (WCAG 1.4.1); */\n/* tone is decorative reinforcement only. */\n/* ------------------------------------------------------------------ */\n\nexport type FaceFindingType = 'observation' | 'treatment' | 'concern' | 'note';\n\nexport const FACE_FINDING_TYPES: FaceFindingType[] = [\n 'observation',\n 'treatment',\n 'concern',\n 'note',\n];\n\nexport const FACE_FINDING_MARKER: Record<\n FaceFindingType,\n { shape: MarkerShape; tone: MarkerTone }\n> = {\n observation: { shape: 'circle', tone: 'info' },\n treatment: { shape: 'square', tone: 'success' },\n concern: { shape: 'triangle', tone: 'warning' },\n note: { shape: 'diamond', tone: 'neutral' },\n};\n\nexport interface FaceFinding {\n type: FaceFindingType;\n /** Optional free-text note (PHI — never surfaced in the agent snapshot). */\n note?: string;\n}\n\n/** Findings keyed by zone id. */\nexport type FaceChart = Record<string, FaceFinding[]>;\n\n/* ------------------------------------------------------------------ */\n/* Registration — 6-DOF affine maths (pure). */\n/* */\n/* A `FaceAffine` is the SVG matrix(a b c d e f): x' = a·x + c·y + e, */\n/* y' = b·x + d·y + f. Applied as a `transform` ATTRIBUTE on the mesh */\n/* group, so it never becomes an inline style. */\n/* ------------------------------------------------------------------ */\n\nexport type FaceAffine = readonly [\n number,\n number,\n number,\n number,\n number,\n number,\n];\n\nexport const IDENTITY_AFFINE: FaceAffine = [1, 0, 0, 1, 0, 0];\n\n/** Per-view registration matrices. */\nexport type FaceRegistration = Partial<Record<FaceViewId, FaceAffine>>;\n\nexport function affineToString(m: FaceAffine): string {\n return `matrix(${m[0]} ${m[1]} ${m[2]} ${m[3]} ${m[4]} ${m[5]})`;\n}\n\nexport function applyAffine(m: FaceAffine, p: Point): Point {\n return {\n x: m[0] * p.x + m[2] * p.y + m[4],\n y: m[1] * p.x + m[3] * p.y + m[5],\n };\n}\n\n/** Editable decomposition of an affine about a pivot — what the numeric\n * registration panel exposes. Angles are degrees. */\nexport interface AffineParts {\n scaleX: number;\n scaleY: number;\n /** degrees */\n rotation: number;\n /** degrees */\n shear: number;\n translateX: number;\n translateY: number;\n}\n\nconst DEG = Math.PI / 180;\n\nexport function composeAffine(parts: AffineParts, pivot: Point): FaceAffine {\n const rad = parts.rotation * DEG;\n const ca = Math.cos(rad);\n const sa = Math.sin(rad);\n const k = Math.tan(parts.shear * DEG);\n const a = parts.scaleX * ca;\n const b = parts.scaleX * sa;\n const c = parts.scaleY * (k * ca - sa);\n const d = parts.scaleY * (k * sa + ca);\n const e = parts.translateX + pivot.x - (a * pivot.x + c * pivot.y);\n const f = parts.translateY + pivot.y - (b * pivot.x + d * pivot.y);\n return [a, b, c, d, e, f];\n}\n\nexport function decomposeAffine(m: FaceAffine, pivot: Point): AffineParts {\n const [a, b, c, d, e, f] = m;\n const scaleX = Math.hypot(a, b);\n const rotation = Math.atan2(b, a) / DEG;\n const det = a * d - b * c;\n const scaleY = scaleX === 0 ? 0 : det / scaleX;\n const shear = Math.atan2(a * c + b * d, det) / DEG;\n const translateX = e - pivot.x + (a * pivot.x + c * pivot.y);\n const translateY = f - pivot.y + (b * pivot.x + d * pivot.y);\n return { scaleX, scaleY, rotation, shear, translateX, translateY };\n}\n\n/**\n * Least-squares 6-DOF affine mapping `src` → `dst` (≥3 non-collinear pairs).\n * Independent horizontal + vertical scale fall out of the fit — this is the\n * shared solver behind both manual landmark drag and face-detector auto-fit.\n * Returns the identity if the system is degenerate.\n */\nexport function solveAffine(src: Point[], dst: Point[]): FaceAffine {\n const n = Math.min(src.length, dst.length);\n if (n < 3) return IDENTITY_AFFINE;\n let Sxx = 0;\n let Sxy = 0;\n let Sx = 0;\n let Syy = 0;\n let Sy = 0;\n let bx0 = 0;\n let bx1 = 0;\n let bx2 = 0;\n let by0 = 0;\n let by1 = 0;\n let by2 = 0;\n for (let i = 0; i < n; i++) {\n const s = src[i];\n const t = dst[i];\n if (!s || !t) continue;\n Sxx += s.x * s.x;\n Sxy += s.x * s.y;\n Sx += s.x;\n Syy += s.y * s.y;\n Sy += s.y;\n bx0 += t.x * s.x;\n bx1 += t.x * s.y;\n bx2 += t.x;\n by0 += t.y * s.x;\n by1 += t.y * s.y;\n by2 += t.y;\n }\n // 3×3 normal matrix N (symmetric); solve N·[a c e]ᵀ = bx and N·[b d f]ᵀ = by.\n const det =\n Sxx * (Syy * n - Sy * Sy) -\n Sxy * (Sxy * n - Sy * Sx) +\n Sx * (Sxy * Sy - Syy * Sx);\n if (Math.abs(det) < 1e-9) return IDENTITY_AFFINE;\n // Inverse of the symmetric 3×3, reused for both right-hand sides.\n const i00 = Syy * n - Sy * Sy;\n const i01 = Sx * Sy - Sxy * n;\n const i02 = Sxy * Sy - Sx * Syy;\n const i11 = Sxx * n - Sx * Sx;\n const i12 = Sx * Sxy - Sxx * Sy;\n const i22 = Sxx * Syy - Sxy * Sxy;\n const solve = (\n r0: number,\n r1: number,\n r2: number,\n ): [number, number, number] => [\n (i00 * r0 + i01 * r1 + i02 * r2) / det,\n (i01 * r0 + i11 * r1 + i12 * r2) / det,\n (i02 * r0 + i12 * r1 + i22 * r2) / det,\n ];\n const [a, c, e] = solve(bx0, bx1, bx2);\n const [b, d, f] = solve(by0, by1, by2);\n return [a, b, c, d, e, f];\n}\n","/* ------------------------------------------------------------------ */\n/* FaceScheme — patient-photo face-zone mapping (aesthetic medicine). */\n/* */\n/* A consumer-supplied patient photo renders as the <image> base; the */\n/* clinical face-zone mesh (byte-identical to the platform FaceMap) */\n/* floats on top as transparent CLICK HOTSPOTS with a crisp 1px outline. */\n/* Because the mesh is a canonical schematic and the photo is an */\n/* arbitrary face, the mesh is REGISTERED onto the photo with a 6-DOF */\n/* affine — independent horizontal/vertical scale, rotation, shear and */\n/* offset — driven by draggable landmark points, accessible numeric */\n/* sliders, or the optional in-kit face detector (lazy-loaded). Findings */\n/* are marked with the shared AnnotationMarker glyph (SHAPE carries the */\n/* meaning, never colour alone) and ride the registration transform. */\n/* */\n/* PHI: the kit is render-only. The photo arrives as a prop and is only */\n/* ever drawn into <image href>; it is never fetched, persisted, logged, */\n/* or exposed through the agent snapshot. The detector runs on-device. */\n/* ------------------------------------------------------------------ */\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type ChangeEvent,\n type KeyboardEvent,\n type PointerEvent as ReactPointerEvent,\n} from 'react';\nimport * as RadixTooltip from '@radix-ui/react-tooltip';\nimport { cva } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { useAgentRegistration } from '../../agent/registry';\nimport {\n AnnotationMarker,\n markerPath,\n type MarkerTone,\n} from '../annotation-marker';\nimport { Button } from '../button';\nimport { Slider } from '../slider';\nimport { RadioGroup, Radio } from '../radio-group';\nimport { faceSchemeAgent } from './face-scheme.agent';\nimport { detectFaceLandmarks } from './detect-face-landmarks';\nimport { detectFaceYuNet } from './detect-face-yunet';\nimport {\n FACE_VIEWS,\n FACE_VIEW_IDS,\n FACE_FINDING_MARKER,\n FACE_FINDING_TYPES,\n IDENTITY_AFFINE,\n affineToString,\n applyAffine,\n composeAffine,\n decomposeAffine,\n solveAffine,\n type FaceAffine,\n type FaceChart,\n type FaceFindingType,\n type FaceRegistration,\n type FaceViewId,\n type FaceZone,\n type Point,\n} from './face-scheme-data';\n\n/* ------------------------------------------------------------------ */\n/* Public types */\n/* ------------------------------------------------------------------ */\n\nexport type FaceDetectStatus =\n | 'idle'\n | 'loading'\n | 'detecting'\n | 'done'\n | 'no-face'\n | 'error';\n\nexport interface FaceDetectOptions {\n /** FRONT view (MediaPipe): base URL for the wasm fileset (defaults to the jsDelivr CDN). */\n wasmBaseUrl?: string;\n /** FRONT view (MediaPipe): URL of the `face_landmarker.task` model (defaults to Google's CDN). */\n modelAssetPath?: string;\n /** PROFILE views (YuNet): ESM bundle URL for `onnxruntime-web` (defaults to the jsDelivr CDN). */\n ortModuleUrl?: string;\n /** PROFILE views (YuNet): base URL for the ORT wasm fileset (defaults to the jsDelivr CDN). */\n ortWasmBaseUrl?: string;\n /** PROFILE views (YuNet): URL of the YuNet `.onnx` model (defaults to the kit's bundled copy). */\n yunetModelUrl?: string;\n}\n\n/**\n * Reduce YuNet's five keypoints to the profile fit triangle. On a 90° profile\n * the contralateral eye / mouth-corner are hallucinated FORWARD (toward the\n * nose), so the VISIBLE ones are the keypoints farther from the nose tip in x.\n * The resulting eye / nose-tip / mouth ids match the profile `PROFILE_LANDMARKS`.\n */\nfunction profileTriangleFromYuNet(\n pairs: Array<{ id: string; photo: Point }>,\n): Array<{ id: string; photo: Point }> | null {\n const photoOf = (id: string) => pairs.find((p) => p.id === id)?.photo;\n const nose = photoOf('nose-tip');\n const rightEye = photoOf('right-eye');\n const leftEye = photoOf('left-eye');\n const rightMouth = photoOf('mouth-right');\n const leftMouth = photoOf('mouth-left');\n if (!nose || !rightEye || !leftEye || !rightMouth || !leftMouth) return null;\n const farther = (a: Point, b: Point) =>\n Math.abs(a.x - nose.x) >= Math.abs(b.x - nose.x) ? a : b;\n return [\n { id: 'eye', photo: farther(rightEye, leftEye) },\n { id: 'nose-tip', photo: nose },\n { id: 'mouth', photo: farther(rightMouth, leftMouth) },\n ];\n}\n\nexport interface FaceSchemeProps {\n /** Opaque instance id — emitted as `data-component-id` for the agent registry. */\n id?: string;\n /**\n * Consumer-supplied patient photo for the FRONT view (shorthand for\n * `viewPhotos.front`). RASTER ONLY (object-URL / `data:image` / app CDN\n * raster) — never an `.svg`. PHI: the kit renders it into an SVG\n * `<image href>` and does nothing else (no fetch, persist, log, transmit) and\n * never exposes `src` via the agent snapshot.\n */\n patientPhoto?: { src: string; altKey?: string };\n /** Per-view photos — each view shows its own image, swapped by the switcher. */\n viewPhotos?: Partial<Record<FaceViewId, { src: string; altKey?: string }>>;\n /** Controlled active view (`front` / `profile-left` / `profile-right`). */\n view?: FaceViewId;\n /** Uncontrolled initial view (default `'front'`). */\n defaultView?: FaceViewId;\n /** Fired when the view switcher changes the active view. */\n onViewChange?: (view: FaceViewId) => void;\n /** Show the built-in radio view switcher (default `true`). */\n showViewSwitcher?: boolean;\n /** `'interactive'` enables zone selection, finding edits and registration; `'display'` is read-only. */\n mode?: 'interactive' | 'display';\n /** Controlled findings — `Record<zoneId, FaceFinding[]>`. */\n value?: FaceChart;\n /** Uncontrolled initial findings. */\n defaultValue?: FaceChart;\n /** Fired whenever the chart changes. */\n onChange?: (next: FaceChart) => void;\n /** Fired when a zone is activated (click / Enter / Space). */\n onSelectZone?: (zoneId: string) => void;\n /** The finding type a zone-click applies in interactive mode (toggles it). */\n activeFinding?: FaceFindingType;\n /** Controlled per-view registration affine. */\n registration?: FaceRegistration;\n /** Uncontrolled initial registration. */\n defaultRegistration?: FaceRegistration;\n /** Fired whenever the registration changes. */\n onRegistrationChange?: (next: FaceRegistration) => void;\n /** `'off'` hides the fit controls; `'adjust'` shows landmark handles + numeric sliders + auto-fit. */\n registrationMode?: 'off' | 'adjust';\n /**\n * Enable the in-kit, on-device face detector for Auto-fit. The front view\n * uses MediaPipe Face Landmarker; the two profile views use YuNet (which,\n * unlike the near-frontal FaceMesh, locates a 90° profile). Both runtimes and\n * their models are lazy-loaded only when detection actually runs, so nothing\n * weighs on the base bundle. Pass options to point at self-hosted models.\n */\n autoDetect?: boolean | FaceDetectOptions;\n /** Observe the detector lifecycle. */\n onDetectStatus?: (status: FaceDetectStatus) => void;\n /**\n * Opt-in: show an on-device photo-capture affordance — a live camera\n * viewfinder (with a file-upload fallback). The captured frame is rendered\n * into the same `<image>` base and emitted via `onCapture`; it is never\n * uploaded, persisted, or logged by the kit. The camera stream is torn down\n * the instant a frame is frozen and on cancel / unmount. Independent of\n * `mode` — set it wherever the practitioner should be able to (re)take the\n * photo.\n */\n allowCapture?: boolean;\n /** Camera to request for capture (default `'user'` — a face self-portrait). */\n captureFacingMode?: 'user' | 'environment';\n /**\n * Fired when a photo is captured or chosen — a `data:` URL, its intrinsic\n * pixel size, and the view it belongs to. PHI: the consumer owns persistence;\n * the kit only renders it.\n */\n onCapture?: (photo: {\n src: string;\n width: number;\n height: number;\n view: FaceViewId;\n }) => void;\n /** Show the finding-type legend below the figure. */\n legend?: boolean;\n /** Accessible label override for the chart region. */\n ariaLabel?: string;\n /** Extra class names on the wrapper. */\n className?: string;\n}\n\nexport interface FaceSchemeHandle {\n /** Focus a zone by id. */\n focusZone: (zoneId: string) => void;\n /** Read the current chart state. */\n getChart: () => FaceChart;\n /** Read the findings on one zone (empty array if none). */\n getZone: (zoneId: string) => FaceChart[string];\n /** Read the zone ids carrying a given finding type. */\n zonesWith: (type: FaceFindingType) => string[];\n /** Add a finding to a zone (no-op if already present). */\n setFinding: (zoneId: string, type: FaceFindingType, note?: string) => void;\n /** Remove a finding type from a zone. */\n removeFinding: (zoneId: string, type: FaceFindingType) => void;\n /** Clear every finding from a zone. */\n clearZone: (zoneId: string) => void;\n /** Read the active-view registration affine. */\n getRegistration: () => FaceAffine;\n /** Set the active-view registration affine. */\n setRegistration: (m: FaceAffine) => void;\n /** Reset the active-view registration to the identity. */\n resetRegistration: () => void;\n /** Solve + apply an affine from canonical-anchor → photo-point pairs. */\n fitToLandmarks: (pairs: { id: string; photo: Point }[]) => void;\n /** Run the on-device detector on the current photo and auto-fit. Resolves\n * `true` on a successful fit, `false` if detection is disabled / finds no face. */\n detect: () => Promise<boolean>;\n /** Start the on-device camera viewfinder (no-op if capture is disabled or the\n * camera API is unavailable — emits an error status in that case). */\n startCapture: () => void;\n /** Stop the camera viewfinder without capturing. */\n cancelCapture: () => void;\n /** Read the active view id (`front` / `profile-left` / `profile-right`). */\n getView: () => FaceViewId;\n /** Switch the active view. */\n setView: (view: FaceViewId) => void;\n}\n\n/* ------------------------------------------------------------------ */\n/* Capture (on-device camera) — privacy-first, mirrors DocumentScanner */\n/* ------------------------------------------------------------------ */\n\nexport type FaceCaptureState = 'idle' | 'requesting' | 'streaming' | 'error';\nexport type FaceCaptureError =\n | 'permission-denied'\n | 'no-camera'\n | 'not-readable'\n | 'insecure-context'\n | 'capture-failed';\n\n/** True when getUserMedia is reachable (secure context + API present). */\nfunction mediaDevicesAvailable(): boolean {\n return (\n typeof navigator !== 'undefined' &&\n typeof navigator.mediaDevices !== 'undefined' &&\n typeof navigator.mediaDevices.getUserMedia === 'function'\n );\n}\n\n/** Map a getUserMedia rejection (a DOMException) to our error kind. */\nfunction classifyMediaError(err: unknown): FaceCaptureError {\n const name = err instanceof Error ? err.name : '';\n if (name === 'NotAllowedError' || name === 'SecurityError')\n return 'permission-denied';\n if (name === 'NotFoundError' || name === 'OverconstrainedError')\n return 'no-camera';\n if (name === 'NotReadableError' || name === 'AbortError')\n return 'not-readable';\n return 'capture-failed';\n}\n\nconst CAPTURE_ERROR_KEY: Record<FaceCaptureError, string> = {\n 'permission-denied': 'faceScheme.capture.error.permissionDenied',\n 'no-camera': 'faceScheme.capture.error.noCamera',\n 'not-readable': 'faceScheme.capture.error.notReadable',\n 'insecure-context': 'faceScheme.capture.error.insecureContext',\n 'capture-failed': 'faceScheme.capture.error.captureFailed',\n};\n\n/* ------------------------------------------------------------------ */\n/* Tone → fill class (no inline styles; tokens only) */\n/* ------------------------------------------------------------------ */\n\nconst markerToneFill: Record<MarkerTone, string> = {\n neutral: 'ds:[fill:var(--muted-foreground)]',\n info: 'ds:[fill:var(--info)]',\n success: 'ds:[fill:var(--success)]',\n warning: 'ds:[fill:var(--warning)]',\n error: 'ds:[fill:var(--destructive)]',\n accent: 'ds:[fill:var(--accent)]',\n};\n\n// One fill declaration per zone resolved through compoundVariants. The outline is\n// a constant 1px (non-scaling-stroke) so it stays crisp under the registration\n// scale; the fill only reveals on hover (interactive) or when the zone is marked.\nconst zoneVariants = cva(\n [\n 'ds:[vector-effect:non-scaling-stroke]',\n 'ds:transition-[fill-opacity] ds:[transition-duration:var(--animation-duration)]',\n 'ds:[fill:transparent] ds:[stroke:var(--accent)] ds:[stroke-width:1]',\n ].join(' '),\n {\n variants: {\n interactive: { true: 'ds:cursor-pointer', false: '' },\n marked: { true: '', false: '' },\n },\n compoundVariants: [\n {\n marked: false,\n interactive: true,\n class: 'ds:hover:[fill:var(--accent)] ds:hover:[fill-opacity:0.14]',\n },\n {\n marked: true,\n class: 'ds:[fill:var(--accent)] ds:[fill-opacity:0.18]',\n },\n ],\n defaultVariants: { interactive: false, marked: false },\n },\n);\n\n/* ------------------------------------------------------------------ */\n/* Helpers */\n/* ------------------------------------------------------------------ */\n\nconst SLIDER_RANGES = {\n scaleX: { min: 0.3, max: 1.8, step: 0.01 },\n scaleY: { min: 0.3, max: 1.8, step: 0.01 },\n rotation: { min: -20, max: 20, step: 0.5 },\n shear: { min: -20, max: 20, step: 0.5 },\n translateX: { min: -240, max: 240, step: 1 },\n translateY: { min: -240, max: 240, step: 1 },\n} as const;\n\ntype SliderField = keyof typeof SLIDER_RANGES;\n// Order mirrors the 6 DOF the pointer-drag fit can reach, so the keyboard path\n// has full parity with the landmark handles (size → angle/skew → position).\nconst SLIDER_FIELDS: SliderField[] = [\n 'scaleX',\n 'scaleY',\n 'rotation',\n 'shear',\n 'translateX',\n 'translateY',\n];\n\n/* ------------------------------------------------------------------ */\n/* FaceScheme */\n/* ------------------------------------------------------------------ */\n\nexport const FaceScheme = forwardRef<FaceSchemeHandle, FaceSchemeProps>(\n (\n {\n id,\n patientPhoto,\n viewPhotos,\n view: viewProp,\n defaultView = 'front',\n onViewChange,\n showViewSwitcher = true,\n mode = 'interactive',\n value,\n defaultValue,\n onChange,\n onSelectZone,\n activeFinding,\n registration,\n defaultRegistration,\n onRegistrationChange,\n registrationMode = 'off',\n autoDetect,\n onDetectStatus,\n allowCapture = false,\n captureFacingMode = 'user',\n onCapture,\n legend = false,\n ariaLabel,\n className,\n },\n ref,\n ) => {\n const { t } = useTranslation();\n const reactId = useId();\n const instanceId = id ?? reactId;\n const rootRef = useRef<HTMLDivElement>(null);\n const svgRef = useRef<SVGSVGElement>(null);\n const videoRef = useRef<HTMLVideoElement>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n // The persistent Take/Retake button — focus re-homes here when the\n // streaming viewfinder (which held focus) unmounts on capture / cancel.\n const captureButtonRef = useRef<HTMLButtonElement>(null);\n const unmountedRef = useRef(false);\n\n /* ---- Active view (controlled / uncontrolled, radio-driven) ---- */\n const isViewControlled = viewProp !== undefined;\n const [internalView, setInternalView] = useState<FaceViewId>(\n viewProp ?? defaultView,\n );\n const viewId = isViewControlled ? viewProp : internalView;\n const viewIdRef = useRef<FaceViewId>(viewId);\n viewIdRef.current = viewId;\n const setView = useCallback(\n (next: FaceViewId) => {\n if (!isViewControlled) setInternalView(next);\n onViewChange?.(next);\n },\n [isViewControlled, onViewChange],\n );\n\n const view = FACE_VIEWS[viewId];\n const [, , vbW, vbH] = view.viewBox.split(' ').map(Number) as [\n number,\n number,\n number,\n number,\n ];\n const interactive = mode === 'interactive';\n const adjusting = interactive && registrationMode === 'adjust';\n\n /* ---- Findings state (controlled / uncontrolled) ---- */\n const isControlled = value !== undefined;\n const [internal, setInternal] = useState<FaceChart>(defaultValue ?? {});\n const chart = isControlled ? value : internal;\n const chartRef = useRef<FaceChart>(chart);\n chartRef.current = chart;\n\n const commitChart = useCallback(\n (next: FaceChart) => {\n if (!isControlled) setInternal(next);\n onChange?.(next);\n },\n [isControlled, onChange],\n );\n\n const addFinding = useCallback(\n (zoneId: string, type: FaceFindingType, note?: string) => {\n const current = chartRef.current[zoneId] ?? [];\n if (current.some((f) => f.type === type)) return;\n commitChart({\n ...chartRef.current,\n [zoneId]: [...current, { type, note }],\n });\n },\n [commitChart],\n );\n const removeFindingFn = useCallback(\n (zoneId: string, type: FaceFindingType) => {\n const current = chartRef.current[zoneId] ?? [];\n const next = current.filter((f) => f.type !== type);\n const copy = { ...chartRef.current };\n if (next.length > 0) copy[zoneId] = next;\n else delete copy[zoneId];\n commitChart(copy);\n },\n [commitChart],\n );\n const toggleFinding = useCallback(\n (zoneId: string, type: FaceFindingType) => {\n const has = (chartRef.current[zoneId] ?? []).some(\n (f) => f.type === type,\n );\n if (has) removeFindingFn(zoneId, type);\n else addFinding(zoneId, type);\n },\n [addFinding, removeFindingFn],\n );\n\n /* ---- Registration state (controlled / uncontrolled) ---- */\n const isRegControlled = registration !== undefined;\n const [internalReg, setInternalReg] = useState<FaceRegistration>(\n defaultRegistration ?? {},\n );\n const regMap = isRegControlled ? registration : internalReg;\n const activeAffine = regMap[viewId] ?? IDENTITY_AFFINE;\n const affineRef = useRef<FaceAffine>(activeAffine);\n affineRef.current = activeAffine;\n // Mirror of the full registration map so commits never read a stale closure.\n const affineRefMap = useRef<FaceRegistration>(regMap);\n affineRefMap.current = regMap;\n\n const commitReg = useCallback(\n (m: FaceAffine) => {\n const next = { ...affineRefMap.current, [viewId]: m };\n if (!isRegControlled) setInternalReg(next);\n onRegistrationChange?.(next);\n },\n [isRegControlled, onRegistrationChange, viewId],\n );\n\n const parts = useMemo(\n () => decomposeAffine(activeAffine, view.pivot),\n [activeAffine, view.pivot],\n );\n\n // The right-profile mesh is flipped about the frame centre at render time,\n // so landmark photo points (detector output, drag targets) must be flipped\n // to / from the un-mirrored space the affine is solved in. Self-inverse.\n const mirrorPt = useCallback(\n (p: Point): Point => (view.mirror ? { x: vbW - p.x, y: p.y } : p),\n [view.mirror, vbW],\n );\n\n /* ---- Landmark handle targets (pointer fitting) ---- */\n const [targets, setTargets] = useState<Point[]>(() =>\n view.landmarks.map((lm) => mirrorPt(applyAffine(activeAffine, lm))),\n );\n // Keep handles riding the mesh when the affine changes for any reason other\n // than an in-progress drag (slider, auto-fit, reset, external prop).\n const draggingRef = useRef<number | null>(null);\n useEffect(() => {\n if (draggingRef.current !== null) return;\n setTargets(\n view.landmarks.map((lm) => mirrorPt(applyAffine(activeAffine, lm))),\n );\n }, [activeAffine, view.landmarks, mirrorPt]);\n\n const fitFromPairs = useCallback(\n (pairs: { id: string; photo: Point }[]) => {\n const src: Point[] = [];\n const dst: Point[] = [];\n for (const pair of pairs) {\n const anchor = view.landmarks.find((l) => l.id === pair.id);\n if (!anchor) continue;\n src.push({ x: anchor.x, y: anchor.y });\n dst.push(mirrorPt(pair.photo));\n }\n if (src.length >= 3) commitReg(solveAffine(src, dst));\n },\n [commitReg, view.landmarks, mirrorPt],\n );\n\n const toSvgPoint = useCallback(\n (clientX: number, clientY: number): Point => {\n const svg = svgRef.current;\n if (!svg) return { x: 0, y: 0 };\n const pt = svg.createSVGPoint();\n pt.x = clientX;\n pt.y = clientY;\n const ctm = svg.getScreenCTM();\n if (!ctm) return { x: 0, y: 0 };\n const local = pt.matrixTransform(ctm.inverse());\n return { x: local.x, y: local.y };\n },\n [],\n );\n\n const handlePointerDown = useCallback(\n (index: number) => (e: ReactPointerEvent<SVGCircleElement>) => {\n draggingRef.current = index;\n e.currentTarget.setPointerCapture(e.pointerId);\n },\n [],\n );\n const handlePointerMove = useCallback(\n (e: ReactPointerEvent<SVGCircleElement>) => {\n const index = draggingRef.current;\n if (index === null) return;\n const p = toSvgPoint(e.clientX, e.clientY);\n const next = targets.map((tg, i) => (i === index ? p : tg));\n setTargets(next);\n const src = view.landmarks.map((lm) => ({ x: lm.x, y: lm.y }));\n // `next` are rendered (possibly mirrored) positions — solve in\n // un-mirrored space so the mirror transform lands them back correctly.\n commitReg(solveAffine(src, next.map(mirrorPt)));\n },\n [targets, toSvgPoint, view.landmarks, commitReg, mirrorPt],\n );\n const handlePointerUp = useCallback(() => {\n draggingRef.current = null;\n }, []);\n\n /* ---- Capture (on-device camera + file fallback) ---- */\n const [captureState, setCaptureState] = useState<FaceCaptureState>('idle');\n const [captureError, setCaptureError] = useState<FaceCaptureError | null>(\n null,\n );\n // Captured frames are keyed by view — each view keeps its own photo.\n const [capturedByView, setCapturedByView] = useState<\n Partial<Record<FaceViewId, string>>\n >({});\n // Per-view photo: explicit map wins, else `patientPhoto` is the front shorthand.\n const viewPhoto =\n viewPhotos?.[viewId] ?? (viewId === 'front' ? patientPhoto : undefined);\n // The captured frame supersedes the prop photo (uncontrolled-friendly).\n const effectiveSrc = capturedByView[viewId] ?? viewPhoto?.src;\n\n // Stream teardown — the single most important privacy path.\n const stopStream = useCallback(() => {\n streamRef.current?.getTracks().forEach((track) => track.stop());\n streamRef.current = null;\n const video = videoRef.current;\n if (video) video.srcObject = null;\n }, []);\n\n const cancelCapture = useCallback(() => {\n stopStream();\n setCaptureState('idle');\n }, [stopStream]);\n\n // Start the camera — user gesture only, never auto on mount.\n const startCapture = useCallback(async () => {\n if (!allowCapture) return;\n if (!mediaDevicesAvailable()) {\n setCaptureError('insecure-context');\n setCaptureState('error');\n return;\n }\n setCaptureError(null);\n setCaptureState('requesting');\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n video: { facingMode: captureFacingMode },\n audio: false,\n });\n // Guard an unmount between the permission prompt and resolution.\n if (unmountedRef.current) {\n stream.getTracks().forEach((track) => track.stop());\n return;\n }\n streamRef.current = stream;\n const video = videoRef.current;\n if (video) {\n try {\n video.srcObject = stream;\n } catch {\n /* some envs reject a non-native MediaStream on srcObject; the\n stream is still held + torn down correctly */\n }\n const playResult = video.play?.();\n if (playResult && typeof playResult.catch === 'function') {\n playResult.catch(() => {\n /* autoplay can reject silently; the Capture button still works */\n });\n }\n }\n setCaptureState('streaming');\n } catch (err: unknown) {\n stopStream();\n setCaptureError(classifyMediaError(err));\n setCaptureState('error');\n }\n }, [allowCapture, captureFacingMode, stopStream]);\n\n const commitCaptured = useCallback(\n (src: string, width: number, height: number) => {\n setCapturedByView((prev) => ({ ...prev, [viewId]: src }));\n onCapture?.({ src, width, height, view: viewId });\n },\n [onCapture, viewId],\n );\n\n // Freeze the current frame onto a canvas, release the camera, emit it.\n const capturePhoto = useCallback(() => {\n const video = videoRef.current;\n const vw = video?.videoWidth || video?.clientWidth || 0;\n const vh = video?.videoHeight || video?.clientHeight || 0;\n if (!video || video.readyState < 2 || vw === 0 || vh === 0) {\n setCaptureError('capture-failed');\n setCaptureState('error');\n return;\n }\n const canvas = document.createElement('canvas');\n canvas.width = vw;\n canvas.height = vh;\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n setCaptureError('capture-failed');\n setCaptureState('error');\n return;\n }\n try {\n ctx.drawImage(video, 0, 0, vw, vh);\n } catch {\n setCaptureError('capture-failed');\n setCaptureState('error');\n return;\n }\n // Release the camera the instant the frame is frozen — LED goes dark.\n stopStream();\n commitCaptured(canvas.toDataURL('image/jpeg', 0.92), vw, vh);\n setCaptureState('idle');\n }, [stopStream, commitCaptured]);\n\n // File fallback (and the desktop path where no camera is wired up).\n const onPickFile = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n e.target.value = '';\n if (!file) return;\n const reader = new FileReader();\n reader.onload = () => {\n const src = typeof reader.result === 'string' ? reader.result : '';\n if (!src) return;\n const img = new Image();\n img.onload = () =>\n commitCaptured(src, img.naturalWidth, img.naturalHeight);\n img.onerror = () => commitCaptured(src, vbW, vbH);\n img.src = src;\n };\n reader.readAsDataURL(file);\n },\n [commitCaptured, vbW, vbH],\n );\n\n // Privacy: tear the camera down on unmount, whatever the phase.\n useEffect(() => {\n unmountedRef.current = false;\n return () => {\n unmountedRef.current = true;\n streamRef.current?.getTracks().forEach((track) => track.stop());\n streamRef.current = null;\n };\n }, []);\n\n const captureStreaming = captureState === 'streaming';\n\n // When the viewfinder tears down (capture / cancel), it unmounts the button\n // that held focus — re-home focus to the persistent Take/Retake button so a\n // keyboard user isn't dropped to <body>.\n const wasStreamingRef = useRef(false);\n useEffect(() => {\n if (wasStreamingRef.current && !captureStreaming) {\n captureButtonRef.current?.focus();\n }\n wasStreamingRef.current = captureStreaming;\n }, [captureStreaming]);\n\n /* ---- Detection ---- */\n const [detectStatus, setDetectStatus] = useState<FaceDetectStatus>('idle');\n const setStatus = useCallback(\n (s: FaceDetectStatus) => {\n setDetectStatus(s);\n onDetectStatus?.(s);\n },\n [onDetectStatus],\n );\n const runDetect = useCallback(async (): Promise<boolean> => {\n if (!autoDetect || !effectiveSrc || view.landmarks.length === 0)\n return false;\n const options = typeof autoDetect === 'object' ? autoDetect : {};\n const onStage = (stage: 'loading' | 'detecting') =>\n setStatus(stage === 'detecting' ? 'detecting' : 'loading');\n try {\n setStatus('loading');\n // FRONT: MediaPipe FaceMesh (accurate, non-collinear eyes/nose/chin).\n // PROFILE: MediaPipe collapses a 90° pose, so use YuNet and fit the mesh\n // on its visible-side EYE / NOSE-TIP / MOUTH triangle.\n let pairs: Array<{ id: string; photo: Point }> | null;\n if (view.id === 'front') {\n pairs = await detectFaceLandmarks(effectiveSrc, {\n viewWidth: vbW,\n viewHeight: vbH,\n onStage,\n wasmBaseUrl: options.wasmBaseUrl,\n modelAssetPath: options.modelAssetPath,\n });\n } else {\n const raw = await detectFaceYuNet(effectiveSrc, {\n viewWidth: vbW,\n viewHeight: vbH,\n onStage,\n ortModuleUrl: options.ortModuleUrl,\n ortWasmBaseUrl: options.ortWasmBaseUrl,\n modelUrl: options.yunetModelUrl,\n });\n pairs = raw && profileTriangleFromYuNet(raw);\n }\n if (!pairs || pairs.length < 3) {\n setStatus('no-face');\n return false;\n }\n fitFromPairs(pairs);\n setStatus('done');\n return true;\n } catch {\n setStatus('error');\n return false;\n }\n }, [\n autoDetect,\n effectiveSrc,\n view.id,\n view.landmarks,\n vbW,\n vbH,\n setStatus,\n fitFromPairs,\n ]);\n\n /* ---- Imperative handle + agent registration ---- */\n const focusZoneEl = useCallback((zoneId: string) => {\n rootRef.current\n ?.querySelector<SVGGElement>(`[data-zone=\"${zoneId}\"]`)\n ?.focus();\n }, []);\n\n const handle = useMemo<FaceSchemeHandle>(\n () => ({\n focusZone: focusZoneEl,\n getChart: () => chartRef.current,\n getZone: (zoneId) => chartRef.current[zoneId] ?? [],\n zonesWith: (type) =>\n Object.entries(chartRef.current)\n .filter(([, findings]) => findings.some((f) => f.type === type))\n .map(([zoneId]) => zoneId),\n setFinding: addFinding,\n removeFinding: removeFindingFn,\n clearZone: (zoneId) => {\n if (!chartRef.current[zoneId]) return;\n const copy = { ...chartRef.current };\n delete copy[zoneId];\n commitChart(copy);\n },\n getRegistration: () => affineRef.current,\n setRegistration: (m) => commitReg(m),\n resetRegistration: () => commitReg(IDENTITY_AFFINE),\n fitToLandmarks: fitFromPairs,\n detect: runDetect,\n startCapture: () => void startCapture(),\n cancelCapture,\n getView: () => viewIdRef.current,\n setView,\n }),\n [\n focusZoneEl,\n addFinding,\n removeFindingFn,\n commitChart,\n commitReg,\n fitFromPairs,\n runDetect,\n startCapture,\n cancelCapture,\n setView,\n ],\n );\n useImperativeHandle(ref, () => handle, [handle]);\n useAgentRegistration(faceSchemeAgent, handle, instanceId);\n\n /* ---- Zone label ---- */\n const zoneLabel = useCallback(\n (zone: FaceZone): string => {\n const name = t(`faceScheme.zone.${zone.labelKey}`);\n const findings = chartRef.current[zone.id] ?? [];\n if (findings.length === 0) return name;\n const findingNames = findings\n .map((f) => t(`faceScheme.finding.${f.type}`))\n .join(', ');\n return t('faceScheme.zoneWithFindings', {\n zone: name,\n findings: findingNames,\n });\n },\n [t],\n );\n\n const handleActivate = useCallback(\n (zone: FaceZone) => {\n onSelectZone?.(zone.id);\n if (interactive && activeFinding) toggleFinding(zone.id, activeFinding);\n },\n [interactive, activeFinding, toggleFinding, onSelectZone],\n );\n\n const photoAlt = viewPhoto?.altKey\n ? t(viewPhoto.altKey)\n : t('faceScheme.photo.defaultAlt');\n\n // Right profile = left mesh mirrored horizontally about the frame centre.\n const meshTransform = view.mirror\n ? `translate(${vbW} 0) scale(-1 1) ${affineToString(activeAffine)}`\n : affineToString(activeAffine);\n\n const updatePart = useCallback(\n (field: SliderField, val: number) => {\n commitReg(composeAffine({ ...parts, [field]: val }, view.pivot));\n },\n [commitReg, parts, view.pivot],\n );\n\n // Auto-detect runs wherever the active view has fit landmarks (all three\n // views do — the front uses eye/nose/chin, the profiles use midline\n // cephalometric points the detector tracks even on a 90° profile).\n const detectEnabled =\n Boolean(autoDetect) && Boolean(effectiveSrc) && view.landmarks.length > 0;\n // Capture is its own opt-in — available regardless of `mode` so the\n // practitioner can (re)take the photo on any surface that allows it.\n const captureEnabled = allowCapture;\n\n /* ---- Render ---- */\n return (\n <div\n ref={rootRef}\n data-component=\"face-scheme\"\n data-component-id={instanceId}\n data-view={viewId}\n className={['ds:flex ds:flex-col ds:gap-[var(--spacing-md)]', className]\n .filter(Boolean)\n .join(' ')}\n role=\"group\"\n aria-label={ariaLabel ?? t('faceScheme.ariaLabel')}\n >\n {/* View switcher — front / left profile / right profile. */}\n {showViewSwitcher && FACE_VIEW_IDS.length > 1 && (\n <RadioGroup\n label={t('faceScheme.viewSwitcherLabel')}\n variant=\"horizontal\"\n value={viewId}\n onValueChange={(v) => setView(v as FaceViewId)}\n >\n {FACE_VIEW_IDS.map((vid) => (\n <Radio\n key={vid}\n value={vid}\n label={t(`faceScheme.view.${FACE_VIEWS[vid].labelKey}`)}\n />\n ))}\n </RadioGroup>\n )}\n\n {/* Live camera viewfinder — replaces the figure while streaming. */}\n {captureStreaming && (\n <div\n data-testid=\"face-scheme-viewfinder\"\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n >\n {/* eslint-disable-next-line jsx-a11y/media-has-caption -- live self-view, not media content */}\n <video\n ref={videoRef}\n autoPlay\n playsInline\n muted\n aria-label={t('faceScheme.capture.viewfinder')}\n className=\"ds:mx-auto ds:block ds:w-full ds:max-w-[28rem] ds:[border-radius:var(--radius-md)] ds:[transform:scaleX(-1)]\"\n />\n <div className=\"ds:flex ds:flex-wrap ds:justify-center ds:gap-[var(--spacing-xs)]\">\n <Button size=\"sm\" intent=\"primary\" onClick={() => capturePhoto()}>\n {t('faceScheme.capture.capture')}\n </Button>\n <Button size=\"sm\" intent=\"outline\" onClick={cancelCapture}>\n {t('faceScheme.capture.cancel')}\n </Button>\n </div>\n </div>\n )}\n\n {!captureStreaming && (\n <RadixTooltip.Provider delayDuration={200}>\n <svg\n ref={svgRef}\n data-testid=\"face-scheme-svg\"\n viewBox={view.viewBox}\n role=\"group\"\n aria-label={t(`faceScheme.view.${view.labelKey}`)}\n className=\"ds:mx-auto ds:block ds:h-auto ds:w-full ds:max-w-[28rem] ds:touch-none ds:[border-radius:var(--radius-md)]\"\n >\n {effectiveSrc && (\n <image\n href={effectiveSrc}\n x={0}\n y={0}\n width={vbW}\n height={vbH}\n preserveAspectRatio=\"xMidYMid slice\"\n role=\"img\"\n aria-label={photoAlt}\n />\n )}\n\n {/* Registered mesh — the affine maps the canonical zones onto the photo. */}\n <g transform={meshTransform}>\n {/* The dashed face-contour is a guide for the photoless schematic;\n with a photo the real face outline is visible, so drop it. */}\n {!effectiveSrc && (\n <path\n d={view.contour.d}\n aria-hidden=\"true\"\n className=\"ds:[fill:none] ds:[stroke:var(--accent)] ds:[stroke-opacity:0.4] ds:[stroke-width:1] ds:[vector-effect:non-scaling-stroke] ds:[stroke-dasharray:4_5]\"\n />\n )}\n {view.zones.map((zone) => {\n const findings = chart[zone.id] ?? [];\n const marked = findings.length > 0;\n return (\n <RadixTooltip.Root key={zone.id}>\n <RadixTooltip.Trigger asChild>\n <g\n data-zone={zone.id}\n data-side={zone.side}\n role={interactive || onSelectZone ? 'button' : 'img'}\n aria-label={zoneLabel(zone)}\n aria-pressed={\n interactive && activeFinding ? marked : undefined\n }\n // Suppress the UA focus box on the <g>; draw the ring on the\n // zone path itself. forced-colors re-asserts a system stroke.\n className=\"ds:outline-none ds:[&:focus-visible>*:first-child]:[stroke:var(--ring)] ds:[&:focus-visible>*:first-child]:[stroke-width:var(--focus-ring-width)] ds:forced-colors:[&:focus-visible>*:first-child]:[stroke:CanvasText]\"\n tabIndex={interactive || onSelectZone ? 0 : -1}\n onClick={() => handleActivate(zone)}\n onKeyDown={(e: KeyboardEvent<SVGGElement>) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n handleActivate(zone);\n }\n }}\n >\n <path\n d={zone.d}\n className={zoneVariants({ interactive, marked })}\n />\n {findings.map((f, i) => {\n const m = FACE_FINDING_MARKER[f.type];\n const offset = (i - (findings.length - 1) / 2) * 26;\n return (\n <path\n key={f.type}\n d={markerPath(\n m.shape,\n zone.cx + offset,\n zone.cy,\n 11,\n )}\n aria-hidden=\"true\"\n className={markerToneFill[m.tone]}\n />\n );\n })}\n </g>\n </RadixTooltip.Trigger>\n <RadixTooltip.Portal>\n <RadixTooltip.Content\n sideOffset={4}\n className=\"ds:z-50 ds:rounded-[var(--radius-sm)] ds:bg-[var(--popover)] ds:px-2 ds:py-1 ds:text-[length:var(--font-size-xs)] ds:text-[var(--popover-foreground)] ds:shadow-[var(--shadow-md)]\"\n >\n {zoneLabel(zone)}\n <RadixTooltip.Arrow className=\"ds:[fill:var(--popover)]\" />\n </RadixTooltip.Content>\n </RadixTooltip.Portal>\n </RadixTooltip.Root>\n );\n })}\n </g>\n\n {/* Landmark handles — pointer-only fitting affordance (the numeric\n sliders below are the accessible, keyboard-operable equivalent). */}\n {adjusting && effectiveSrc && (\n <g aria-hidden=\"true\" data-testid=\"face-scheme-handles\">\n {view.landmarks.map((lm, i) => {\n const tg =\n targets[i] ?? mirrorPt(applyAffine(activeAffine, lm));\n return (\n <g key={lm.id}>\n <circle\n cx={tg.x}\n cy={tg.y}\n r={13}\n data-handle={lm.id}\n onPointerDown={handlePointerDown(i)}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n className=\"ds:[fill:var(--background)] ds:[fill-opacity:0.9] ds:[stroke:var(--accent)] ds:[stroke-width:2.5] ds:[vector-effect:non-scaling-stroke] ds:cursor-grab ds:touch-none\"\n />\n <path\n d={`M ${tg.x - 5} ${tg.y} H ${tg.x + 5} M ${tg.x} ${tg.y - 5} V ${tg.y + 5}`}\n className=\"ds:[stroke:var(--accent)] ds:[stroke-width:2] ds:[vector-effect:non-scaling-stroke]\"\n pointerEvents=\"none\"\n />\n </g>\n );\n })}\n </g>\n )}\n </svg>\n </RadixTooltip.Provider>\n )}\n\n {/* Capture controls — on-device camera + file fallback. */}\n {captureEnabled && (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n <div className=\"ds:flex ds:flex-wrap ds:justify-center ds:gap-[var(--spacing-xs)]\">\n <Button\n ref={captureButtonRef}\n size=\"sm\"\n intent={effectiveSrc ? 'outline' : 'primary'}\n onClick={() => void startCapture()}\n disabled={captureState === 'requesting'}\n >\n {t(\n effectiveSrc\n ? 'faceScheme.capture.retake'\n : 'faceScheme.capture.take',\n )}\n </Button>\n <Button\n size=\"sm\"\n intent=\"outline\"\n onClick={() => fileInputRef.current?.click()}\n >\n {t('faceScheme.capture.upload')}\n </Button>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n capture={captureFacingMode}\n onChange={onPickFile}\n className=\"ds:sr-only\"\n aria-hidden=\"true\"\n tabIndex={-1}\n data-testid=\"face-scheme-file-input\"\n />\n </div>\n {/* Always mounted so a polite region exists before its first message\n — some SRs miss a live region created in the same mutation as its\n initial text (mirrors the detector-status region below). */}\n <span\n role=\"status\"\n aria-live=\"polite\"\n className=\"ds:text-center ds:text-[length:var(--font-size-xs)] ds:text-[var(--destructive)] ds:empty:hidden\"\n >\n {captureState === 'error' && captureError\n ? t(CAPTURE_ERROR_KEY[captureError])\n : ''}\n </span>\n </div>\n )}\n\n {/* Registration controls — accessible numeric path + actions. */}\n {adjusting && (\n <div\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-sm)]\"\n data-testid=\"face-scheme-registration\"\n >\n <p className=\"ds:m-0 ds:text-[length:var(--font-size-xs)] ds:text-[var(--muted-foreground)]\">\n {t('faceScheme.registration.hint')}\n </p>\n <div className=\"ds:grid ds:grid-cols-1 ds:gap-[var(--spacing-xs)] ds:sm:grid-cols-2\">\n {SLIDER_FIELDS.map((field) => {\n const range = SLIDER_RANGES[field];\n const isScale = field === 'scaleX' || field === 'scaleY';\n return (\n <div\n key={field}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)] ds:text-[length:var(--font-size-xs)] ds:text-[var(--foreground)]\"\n >\n <span>{t(`faceScheme.registration.${field}`)}</span>\n <Slider\n aria-label={t(`faceScheme.registration.${field}`)}\n value={[parts[field]]}\n min={range.min}\n max={range.max}\n step={range.step}\n formatValue={(v) =>\n isScale\n ? `${Math.round(v * 100)}%`\n : field === 'rotation' || field === 'shear'\n ? `${v.toFixed(1)}°`\n : `${Math.round(v)}`\n }\n onValueChange={(vals) => {\n const v = vals[0];\n if (typeof v === 'number') updatePart(field, v);\n }}\n />\n </div>\n );\n })}\n </div>\n <div className=\"ds:flex ds:flex-wrap ds:gap-[var(--spacing-xs)]\">\n {detectEnabled && (\n <Button\n size=\"sm\"\n intent=\"primary\"\n onClick={() => void runDetect()}\n disabled={\n detectStatus === 'loading' || detectStatus === 'detecting'\n }\n >\n {t('faceScheme.registration.autoDetect')}\n </Button>\n )}\n <Button\n size=\"sm\"\n intent=\"outline\"\n onClick={() => commitReg(IDENTITY_AFFINE)}\n >\n {t('faceScheme.registration.reset')}\n </Button>\n {/* Always mounted so a polite region exists before its first\n message — some SRs miss a live region created in the same\n mutation as its initial text. */}\n {detectEnabled && (\n <span\n role=\"status\"\n aria-live=\"polite\"\n className=\"ds:self-center ds:text-[length:var(--font-size-xs)] ds:text-[var(--muted-foreground)]\"\n >\n {detectStatus === 'idle'\n ? ''\n : t(`faceScheme.detect.${detectStatus}`)}\n </span>\n )}\n </div>\n </div>\n )}\n\n {legend && (\n <ul\n aria-label={t('faceScheme.legendLabel')}\n className=\"ds:flex ds:flex-wrap ds:gap-x-4 ds:gap-y-1 ds:p-0 ds:text-[length:var(--font-size-xs)] ds:text-[var(--muted-foreground)]\"\n >\n {FACE_FINDING_TYPES.map((type) => {\n const m = FACE_FINDING_MARKER[type];\n return (\n <li key={type} className=\"ds:flex ds:list-none ds:items-center\">\n <AnnotationMarker\n shape={m.shape}\n tone={m.tone}\n size=\"sm\"\n label={t(`faceScheme.finding.${type}`)}\n />\n </li>\n );\n })}\n </ul>\n )}\n </div>\n );\n },\n);\n\nFaceScheme.displayName = 'FaceScheme';\n"],"names":["ZONE_SCHEMA","VIEW_SCHEMA","FINDING_SCHEMA","faceSchemeAgent","handle","zone","findings","f","args","DEFAULT_VISION_MODULE","DEFAULT_WASM_BASE","DEFAULT_MODEL","ANCHOR_INDICES","landmarkerCache","getLandmarker","visionModuleUrl","wasmBaseUrl","modelAssetPath","onStage","key","cached","promise","mod","fileset","loadImage","src","img","detectFaceLandmarks","options","viewWidth","viewHeight","image","landmarker","face","imgW","imgH","scale","offsetX","offsetY","toView","lm","pairs","id","indices","sx","sy","count","idx","v","DEFAULT_ORT_MODULE","DEFAULT_ORT_WASM_BASE","NET_SIZE","STRIDES","KEYPOINT_IDS","sessionCache","getSession","ortModuleUrl","ortWasmBaseUrl","modelUrl","ort","model","session","defaultModelUrl","preprocess","Tensor","w","h","dw","dh","padX","padY","canvas","ctx","data","n","i","sigmoid","x","decode","out","scoreThreshold","needSigmoid","probe","best","cls","obj","kps","cols","rows","cv","ov","score","c","r","keypoints","j","detectFaceYuNet","tensor","result","coverScale","kx","ky","px","py","FRONT_VIEWBOX","FRONT_PIVOT","FRONT_CONTOUR","FRONT_ZONES","FRONT_LANDMARKS","PROFILE_ZONE_PATHS","PROFILE_CONTOUR","z","PROFILE_ZONES","PROFILE_LANDMARKS","FACE_VIEWS","FACE_VIEW_IDS","FACE_FINDING_TYPES","FACE_FINDING_MARKER","IDENTITY_AFFINE","affineToString","m","applyAffine","p","DEG","composeAffine","parts","pivot","rad","ca","sa","k","a","b","d","e","decomposeAffine","scaleX","rotation","det","scaleY","shear","translateX","translateY","solveAffine","dst","Sxx","Sxy","Sx","Syy","Sy","bx0","bx1","bx2","by0","by1","by2","s","t","i00","i01","i02","i11","i12","i22","solve","r0","r1","r2","profileTriangleFromYuNet","photoOf","_a","nose","rightEye","leftEye","rightMouth","leftMouth","farther","mediaDevicesAvailable","classifyMediaError","err","name","CAPTURE_ERROR_KEY","markerToneFill","zoneVariants","cva","SLIDER_RANGES","SLIDER_FIELDS","FaceScheme","forwardRef","patientPhoto","viewPhotos","viewProp","defaultView","onViewChange","showViewSwitcher","mode","value","defaultValue","onChange","onSelectZone","activeFinding","registration","defaultRegistration","onRegistrationChange","registrationMode","autoDetect","onDetectStatus","allowCapture","captureFacingMode","onCapture","legend","ariaLabel","className","ref","useTranslation","reactId","useId","instanceId","rootRef","useRef","svgRef","videoRef","streamRef","fileInputRef","captureButtonRef","unmountedRef","isViewControlled","internalView","setInternalView","useState","viewId","viewIdRef","setView","useCallback","next","view","vbW","vbH","interactive","adjusting","isControlled","internal","setInternal","chart","chartRef","commitChart","addFinding","zoneId","type","note","current","removeFindingFn","copy","toggleFinding","isRegControlled","internalReg","setInternalReg","regMap","activeAffine","affineRef","affineRefMap","commitReg","useMemo","mirrorPt","targets","setTargets","draggingRef","useEffect","fitFromPairs","pair","anchor","l","toSvgPoint","clientX","clientY","svg","pt","ctm","local","handlePointerDown","index","handlePointerMove","tg","handlePointerUp","captureState","setCaptureState","captureError","setCaptureError","capturedByView","setCapturedByView","viewPhoto","effectiveSrc","stopStream","track","video","cancelCapture","startCapture","stream","playResult","commitCaptured","width","height","prev","capturePhoto","vw","vh","onPickFile","file","reader","captureStreaming","wasStreamingRef","detectStatus","setDetectStatus","setStatus","runDetect","stage","raw","focusZoneEl","_b","useImperativeHandle","useAgentRegistration","zoneLabel","findingNames","handleActivate","photoAlt","meshTransform","updatePart","field","val","detectEnabled","captureEnabled","jsxs","jsx","RadioGroup","vid","Radio","Button","RadixTooltip","marked","offset","markerPath","range","isScale","Slider","vals","AnnotationMarker"],"mappings":"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"}