@ansuman-shukla/whiteboard-mcp 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +7 -7
  2. package/dist/canvas/client.js +3 -3
  3. package/dist/canvas/daemon.js +88 -31
  4. package/dist/canvas/prefs.js +25 -0
  5. package/dist/frontend/assets/{abnfDiagram-VCTEODGH-BxMH5nUQ.js → abnfDiagram-VCTEODGH-DAy37N5S.js} +1 -1
  6. package/dist/frontend/assets/{ar-SA-G6X2FPQ2-C36O-H_9.js → ar-SA-G6X2FPQ2-DuVJczBA.js} +1 -1
  7. package/dist/frontend/assets/{arc-KX2Pfg3Z.js → arc-Bmqio9Z5.js} +1 -1
  8. package/dist/frontend/assets/{architectureDiagram-5GKGNRK7-BHG0460A.js → architectureDiagram-5GKGNRK7-DHR2a9Qn.js} +1 -1
  9. package/dist/frontend/assets/{az-AZ-76LH7QW2-DLRdj82-.js → az-AZ-76LH7QW2-B0s53SVr.js} +1 -1
  10. package/dist/frontend/assets/{bg-BG-XCXSNQG7-DwHBZbGh.js → bg-BG-XCXSNQG7-7_ShzXB8.js} +1 -1
  11. package/dist/frontend/assets/{blockDiagram-I7D4REHJ-4Im-keF2.js → blockDiagram-I7D4REHJ-BiS8RX0X.js} +1 -1
  12. package/dist/frontend/assets/{bn-BD-2XOGV67Q-NKxBfpiH.js → bn-BD-2XOGV67Q-B9EsbDQi.js} +1 -1
  13. package/dist/frontend/assets/{c4Diagram-7LVT6UL2-DEj5I_0u.js → c4Diagram-7LVT6UL2-DHD5C5Za.js} +1 -1
  14. package/dist/frontend/assets/{ca-ES-6MX7JW3Y-Df-0Ntil.js → ca-ES-6MX7JW3Y-Eue3i8IR.js} +1 -1
  15. package/dist/frontend/assets/channel-D5MmJK72.js +1 -0
  16. package/dist/frontend/assets/{chunk-2Q5K7J3B-C1ty76SZ.js → chunk-2Q5K7J3B-BLR2G0ZC.js} +1 -1
  17. package/dist/frontend/assets/{chunk-5VM5RSS4-c2-w2kdf.js → chunk-5VM5RSS4-exFoYWwu.js} +1 -1
  18. package/dist/frontend/assets/{chunk-F27PBJKO-DzpHo7Ik.js → chunk-F27PBJKO-BbwvLX4c.js} +1 -1
  19. package/dist/frontend/assets/{chunk-IMKFNOWR-D8rzP3gH.js → chunk-IMKFNOWR-CLpaMFW-.js} +1 -1
  20. package/dist/frontend/assets/{chunk-JWPE2WC7-7Pq7jJ1J.js → chunk-JWPE2WC7-CME61VGI.js} +1 -1
  21. package/dist/frontend/assets/{chunk-POPQ4Y6H-D_iqQlwU.js → chunk-POPQ4Y6H-6aP85AN4.js} +1 -1
  22. package/dist/frontend/assets/{chunk-SVP7TREG-mpQRweZv.js → chunk-SVP7TREG-DE2-zIGo.js} +1 -1
  23. package/dist/frontend/assets/{chunk-TICWLB2K-rS8nOFdO.js → chunk-TICWLB2K-Cf392zck.js} +1 -1
  24. package/dist/frontend/assets/{chunk-XXDRQBXY-Bgca6qkV.js → chunk-XXDRQBXY-ByUAYhf8.js} +1 -1
  25. package/dist/frontend/assets/classDiagram-ZZMXUADV-CA76Uzex.js +1 -0
  26. package/dist/frontend/assets/classDiagram-v2-VYDZK3BY-CA76Uzex.js +1 -0
  27. package/dist/frontend/assets/{cose-bilkent-JH36ORCC-MmIthuLA.js → cose-bilkent-JH36ORCC-BySav4jh.js} +1 -1
  28. package/dist/frontend/assets/{cs-CZ-2BRQDIVT-l8MKZ6pw.js → cs-CZ-2BRQDIVT-bWR0HLKz.js} +1 -1
  29. package/dist/frontend/assets/{cynefin-OW5HDTMX-BQKgYdPD.js → cynefin-OW5HDTMX-9qcvNUxp.js} +2 -2
  30. package/dist/frontend/assets/{cynefinDiagram-5FMLGOSQ-Bbdj5Qix.js → cynefinDiagram-5FMLGOSQ-mZxFLYoo.js} +1 -1
  31. package/dist/frontend/assets/{da-DK-5WZEPLOC-BoTLO65S.js → da-DK-5WZEPLOC-CVE0h6jZ.js} +1 -1
  32. package/dist/frontend/assets/{dagre-GXQ25YYZ-MocBksnZ.js → dagre-GXQ25YYZ-OuDrM4E1.js} +1 -1
  33. package/dist/frontend/assets/{de-DE-XR44H4JA-CYhALkKQ.js → de-DE-XR44H4JA-ccvZr-ne.js} +1 -1
  34. package/dist/frontend/assets/{diagram-S7CK7UJ4-CSPIPgpo.js → diagram-S7CK7UJ4-S4lvAJEO.js} +1 -1
  35. package/dist/frontend/assets/{diagram-UQ7AKVKN-Du1Em6DO.js → diagram-UQ7AKVKN-BY4O94dU.js} +1 -1
  36. package/dist/frontend/assets/{diagram-VSXAHHWV-9akjVOcd.js → diagram-VSXAHHWV-BW4DhPPR.js} +1 -1
  37. package/dist/frontend/assets/{diagram-VX7I27RA-DMZak4CR.js → diagram-VX7I27RA-J-mmI-TZ.js} +1 -1
  38. package/dist/frontend/assets/{diagram-Z3DM3KII-CaR15msp.js → diagram-Z3DM3KII-B8X6m8AF.js} +1 -1
  39. package/dist/frontend/assets/{ebnfDiagram-PWID7BFC-Bi2N3bQu.js → ebnfDiagram-PWID7BFC-Dm3Zhp7Y.js} +1 -1
  40. package/dist/frontend/assets/{el-GR-BZB4AONW-CLcBAkAQ.js → el-GR-BZB4AONW-DJzYwBnK.js} +1 -1
  41. package/dist/frontend/assets/{erDiagram-RLTQ6QDP-CSQeFRsF.js → erDiagram-RLTQ6QDP-DNtC7uDr.js} +1 -1
  42. package/dist/frontend/assets/{es-ES-U4NZUMDT-3aklWK9t.js → es-ES-U4NZUMDT-CJu5daQ-.js} +1 -1
  43. package/dist/frontend/assets/{eu-ES-A7QVB2H4-BtIGcFzG.js → eu-ES-A7QVB2H4-BIMSRkSC.js} +1 -1
  44. package/dist/frontend/assets/{fa-IR-HGAKTJCU-d7GoocBB.js → fa-IR-HGAKTJCU-C4oIcx0h.js} +1 -1
  45. package/dist/frontend/assets/{fi-FI-Z5N7JZ37-jPJL5iOD.js → fi-FI-Z5N7JZ37-BTiLJQgZ.js} +1 -1
  46. package/dist/frontend/assets/{flowDiagram-HODETNUW-uDFqb36j.js → flowDiagram-HODETNUW-CE8WDjri.js} +1 -1
  47. package/dist/frontend/assets/{fr-FR-RHASNOE6-h9HiRe-a.js → fr-FR-RHASNOE6-hdwVFEtF.js} +1 -1
  48. package/dist/frontend/assets/{ganttDiagram-EL5Y4UJY-P8yT56Rg.js → ganttDiagram-EL5Y4UJY-Cs3ElTb7.js} +1 -1
  49. package/dist/frontend/assets/{gitGraphDiagram-WWUBYQGX-Bs1S0I4P.js → gitGraphDiagram-WWUBYQGX-Mf8ehYkN.js} +1 -1
  50. package/dist/frontend/assets/{gl-ES-HMX3MZ6V-VfhNkRer.js → gl-ES-HMX3MZ6V-Nr5ka5Bw.js} +1 -1
  51. package/dist/frontend/assets/{he-IL-6SHJWFNN-D9QmJ8pe.js → he-IL-6SHJWFNN-Cw2NPrCe.js} +1 -1
  52. package/dist/frontend/assets/{hi-IN-IWLTKZ5I-Ba6r7QOE.js → hi-IN-IWLTKZ5I-DNonK5wq.js} +1 -1
  53. package/dist/frontend/assets/{hu-HU-A5ZG7DT2-AM_Xa1Zo.js → hu-HU-A5ZG7DT2-NU--lFcl.js} +1 -1
  54. package/dist/frontend/assets/{id-ID-SAP4L64H-bT6IyTXi.js → id-ID-SAP4L64H-D6ASRqzV.js} +1 -1
  55. package/dist/frontend/assets/{index-Df2Luesx.js → index-B4ANUftc.js} +6 -6
  56. package/dist/frontend/assets/{index-BDFEvJ3q.js → index-D1iAevn8.js} +22 -22
  57. package/dist/frontend/assets/{index-jPquz6Ud.css → index-DRUkwHmr.css} +1 -1
  58. package/dist/frontend/assets/{infoDiagram-27XIBGKW-06Lm1dWG.js → infoDiagram-27XIBGKW-DomHFR42.js} +1 -1
  59. package/dist/frontend/assets/{ishikawaDiagram-5VMMS53U-C7vdCoDV.js → ishikawaDiagram-5VMMS53U-tZBBbDJo.js} +1 -1
  60. package/dist/frontend/assets/{it-IT-JPQ66NNP-CvstPcAX.js → it-IT-JPQ66NNP-1Sd0wKPV.js} +1 -1
  61. package/dist/frontend/assets/{ja-JP-DBVTYXUO-DAG-iojM.js → ja-JP-DBVTYXUO-BMmgWkgk.js} +1 -1
  62. package/dist/frontend/assets/{journeyDiagram-3NMN7TZE-DvC2BW0H.js → journeyDiagram-3NMN7TZE-B0kLyfhj.js} +1 -1
  63. package/dist/frontend/assets/{kaa-6HZHGXH3-D0yv_CFf.js → kaa-6HZHGXH3-BXGK0-Z-.js} +1 -1
  64. package/dist/frontend/assets/{kab-KAB-ZGHBKWFO-RPz2wE_p.js → kab-KAB-ZGHBKWFO-Cfo0ZdSH.js} +1 -1
  65. package/dist/frontend/assets/{kanban-definition-UXKFOSKX-BMKKQCtM.js → kanban-definition-UXKFOSKX-BOcYyZz4.js} +1 -1
  66. package/dist/frontend/assets/{kk-KZ-P5N5QNE5-CuHixCO8.js → kk-KZ-P5N5QNE5-Dtlpplkm.js} +1 -1
  67. package/dist/frontend/assets/{km-KH-HSX4SM5Z-zlTo5LDV.js → km-KH-HSX4SM5Z-MqIdXxcs.js} +1 -1
  68. package/dist/frontend/assets/{ko-KR-MTYHY66A-CxZjlCW5.js → ko-KR-MTYHY66A-Bb4XVlsM.js} +1 -1
  69. package/dist/frontend/assets/{ku-TR-6OUDTVRD-D1yNiPwK.js → ku-TR-6OUDTVRD-BcSyYg9d.js} +1 -1
  70. package/dist/frontend/assets/{layout-DbvlZj8G.js → layout-BJeF_9D-.js} +1 -1
  71. package/dist/frontend/assets/{linear-DxrJXVAI.js → linear-dObn3NUD.js} +1 -1
  72. package/dist/frontend/assets/{lt-LT-XHIRWOB4-CfrXycjC.js → lt-LT-XHIRWOB4-DAI6qR43.js} +1 -1
  73. package/dist/frontend/assets/{lv-LV-5QDEKY6T-B1HW3C0g.js → lv-LV-5QDEKY6T-CYoIV3Rt.js} +1 -1
  74. package/dist/frontend/assets/{mindmap-definition-YA3MSWOX-BTLJ0LvH.js → mindmap-definition-YA3MSWOX-XeBIs6iF.js} +1 -1
  75. package/dist/frontend/assets/{mr-IN-CRQNXWMA-C14Pfp-t.js → mr-IN-CRQNXWMA-Bt20vJmR.js} +1 -1
  76. package/dist/frontend/assets/{my-MM-5M5IBNSE-DzHC1HCi.js → my-MM-5M5IBNSE-_DDlsOCP.js} +1 -1
  77. package/dist/frontend/assets/{nb-NO-T6EIAALU-CsqehHm6.js → nb-NO-T6EIAALU-CjtoUlus.js} +1 -1
  78. package/dist/frontend/assets/{nl-NL-IS3SIHDZ-CyyoCrrj.js → nl-NL-IS3SIHDZ-BqsAfpik.js} +1 -1
  79. package/dist/frontend/assets/{nn-NO-6E72VCQL-CUbYly9U.js → nn-NO-6E72VCQL-Dhy86xIp.js} +1 -1
  80. package/dist/frontend/assets/{oc-FR-POXYY2M6-BgIsLHkg.js → oc-FR-POXYY2M6-C7K02SYx.js} +1 -1
  81. package/dist/frontend/assets/{pa-IN-N4M65BXN-BVdVX_gQ.js → pa-IN-N4M65BXN-C84hhCZw.js} +1 -1
  82. package/dist/frontend/assets/{pegDiagram-XKGWAZYB-DpvpUpAn.js → pegDiagram-XKGWAZYB-faiTBozA.js} +1 -1
  83. package/dist/frontend/assets/{pica-BtX7lU0X.js → pica-LoLq_5MC.js} +1 -1
  84. package/dist/frontend/assets/{pieDiagram-E7YTZNPT-CvD6M6GB.js → pieDiagram-E7YTZNPT-wlvxLf2a.js} +1 -1
  85. package/dist/frontend/assets/{pl-PL-T2D74RX3-D1l3OoaP.js → pl-PL-T2D74RX3-ZW-H4DY8.js} +1 -1
  86. package/dist/frontend/assets/{pt-BR-5N22H2LF-DK7vau2C.js → pt-BR-5N22H2LF-BBMqZmF2.js} +1 -1
  87. package/dist/frontend/assets/{pt-PT-UZXXM6DQ-B4kavvZg.js → pt-PT-UZXXM6DQ-BwCaNSmi.js} +1 -1
  88. package/dist/frontend/assets/{quadrantDiagram-AXDQQJYC-hV9EF2Vj.js → quadrantDiagram-AXDQQJYC-BCN9ewq-.js} +1 -1
  89. package/dist/frontend/assets/{railroadDiagram-O6MQD6OU-Cvs7v3CR.js → railroadDiagram-O6MQD6OU-lvmlzyRR.js} +1 -1
  90. package/dist/frontend/assets/{requirementDiagram-BXWQKSXE-DYqXjX6a.js → requirementDiagram-BXWQKSXE-BmeDEcSv.js} +1 -1
  91. package/dist/frontend/assets/{ro-RO-JPDTUUEW-DLydQ0UX.js → ro-RO-JPDTUUEW-BzvsVvjB.js} +1 -1
  92. package/dist/frontend/assets/{ru-RU-B4JR7IUQ-D9MTksML.js → ru-RU-B4JR7IUQ-JdVe9IHx.js} +1 -1
  93. package/dist/frontend/assets/{sankeyDiagram-P5KCCOFB-EKnmu8ps.js → sankeyDiagram-P5KCCOFB-CTDGYcEr.js} +1 -1
  94. package/dist/frontend/assets/{sequenceDiagram-WJ2MYXX4-CtdwdTBo.js → sequenceDiagram-WJ2MYXX4-CNoceaee.js} +1 -1
  95. package/dist/frontend/assets/{si-LK-N5RQ5JYF-CiIlF66D.js → si-LK-N5RQ5JYF-D2Nh0CVt.js} +1 -1
  96. package/dist/frontend/assets/{sizeCapture-INFHLROL-BpusYNfP.js → sizeCapture-INFHLROL-DAiQVkO3.js} +1 -1
  97. package/dist/frontend/assets/{sk-SK-C5VTKIMK-Bd3mFHOd.js → sk-SK-C5VTKIMK-CNWoQa-j.js} +1 -1
  98. package/dist/frontend/assets/{sl-SI-NN7IZMDC-BzOaJB82.js → sl-SI-NN7IZMDC-u4K3q8d8.js} +1 -1
  99. package/dist/frontend/assets/{stateDiagram-D77RDMKH-Cnrnl-7w.js → stateDiagram-D77RDMKH--oeV5Glw.js} +1 -1
  100. package/dist/frontend/assets/stateDiagram-v2-MP3YSRHH-BtOx_fS6.js +1 -0
  101. package/dist/frontend/assets/{subset-shared.chunk-04wTTSoh.js → subset-shared.chunk-DNd3Chfx.js} +1 -1
  102. package/dist/frontend/assets/{subset-worker.chunk-CmDpHHTU.js → subset-worker.chunk-JhUeQWWm.js} +1 -1
  103. package/dist/frontend/assets/{sv-SE-XGPEYMSR-1YOlZWHT.js → sv-SE-XGPEYMSR-Dy8jz1l1.js} +1 -1
  104. package/dist/frontend/assets/{swimlanes-42K2YHIH-CVxk4wCy.js → swimlanes-42K2YHIH-DYZ0LDwO.js} +1 -1
  105. package/dist/frontend/assets/swimlanesDiagram-VR7AAH4N-Dwg606Dr.js +8 -0
  106. package/dist/frontend/assets/{ta-IN-2NMHFXQM-DXzBOUQM.js → ta-IN-2NMHFXQM-DNH_eGG8.js} +1 -1
  107. package/dist/frontend/assets/{th-TH-HPSO5L25-CNtAqCz4.js → th-TH-HPSO5L25-99Ows3pJ.js} +1 -1
  108. package/dist/frontend/assets/{timeline-definition-24CTP7MA-QTuI6k1M.js → timeline-definition-24CTP7MA-DmNfcSnf.js} +1 -1
  109. package/dist/frontend/assets/{tr-TR-DEFEU3FU-DZfuanOj.js → tr-TR-DEFEU3FU-aks3fvEC.js} +1 -1
  110. package/dist/frontend/assets/{uk-UA-QMV73CPH-DSEDw_F_.js → uk-UA-QMV73CPH-B_zTvb7d.js} +1 -1
  111. package/dist/frontend/assets/{vennDiagram-4TSXK5OY-Cef9y57F.js → vennDiagram-4TSXK5OY-DPMHEaB2.js} +1 -1
  112. package/dist/frontend/assets/{vi-VN-M7AON7JQ-D8uCgy7c.js → vi-VN-M7AON7JQ-CRVuN8X1.js} +1 -1
  113. package/dist/frontend/assets/{wardleyDiagram-VM6X3IG4-B-bKvEgu.js → wardleyDiagram-VM6X3IG4-SZZcYCsH.js} +1 -1
  114. package/dist/frontend/assets/{xychartDiagram-S5SC5T6Z-dHunz4UA.js → xychartDiagram-S5SC5T6Z-C4mQHHb6.js} +1 -1
  115. package/dist/frontend/assets/{zh-CN-LNUGB5OW-Bsu8p0Xn.js → zh-CN-LNUGB5OW-BDIzfE5j.js} +1 -1
  116. package/dist/frontend/assets/{zh-HK-E62DVLB3-BHnkb745.js → zh-HK-E62DVLB3-OXqItY66.js} +1 -1
  117. package/dist/frontend/assets/{zh-TW-RAJ6MFWO-B7SX_SqG.js → zh-TW-RAJ6MFWO-CWUMG_DZ.js} +1 -1
  118. package/dist/frontend/index.html +2 -2
  119. package/dist/mcp/editor.js +81 -0
  120. package/dist/mcp/guide.js +2 -2
  121. package/dist/mcp/server.js +13 -0
  122. package/dist/mcp/tools.js +29 -21
  123. package/dist/shared/protocol.js +18 -1
  124. package/dist/vscode/whiteboard-mcp-0.1.1.vsix +0 -0
  125. package/package.json +2 -2
  126. package/skills/whiteboard-explainer/SKILL.md +24 -2
  127. package/dist/frontend/assets/channel-vJh0WhW1.js +0 -1
  128. package/dist/frontend/assets/classDiagram-ZZMXUADV-pHTJxSmH.js +0 -1
  129. package/dist/frontend/assets/classDiagram-v2-VYDZK3BY-pHTJxSmH.js +0 -1
  130. package/dist/frontend/assets/stateDiagram-v2-MP3YSRHH-BACuwAg6.js +0 -1
  131. package/dist/frontend/assets/swimlanesDiagram-VR7AAH4N-NVZpf_JZ.js +0 -8
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  <a href="#quick-start">
2
2
  <picture>
3
3
  <source media="(prefers-color-scheme: dark)" srcset="docs/media/readme/hero-dark.svg">
4
- <img src="docs/media/readme/hero-light.svg" width="100%" alt="whiteboard-mcp: a live whiteboard your coding agent draws on. You draw back.">
4
+ <img src="docs/media/readme/hero-light.svg" width="100%" alt="Whiteboard MCP: a live whiteboard your coding agent draws on. You draw back.">
5
5
  </picture>
6
6
  </a>
7
7
 
8
- <p align="center"><b>whiteboard-mcp</b> is a free, open-source <b>Excalidraw whiteboard MCP server</b> that runs locally, even offline. Your coding agent draws diagrams on a live board beside the chat, and sees what you draw back. Works with Claude Code, Codex, Cursor, Gemini CLI, VS Code, Windsurf, Devin and any other MCP client.</p>
8
+ <p align="center"><b>Whiteboard MCP</b> is a free, open-source <b>Excalidraw whiteboard MCP server</b> that runs locally, even offline. Your coding agent draws diagrams on a live board beside the chat, and sees what you draw back. Works with Claude Code, Codex, Cursor, Gemini CLI, VS Code, Windsurf, Devin and any other MCP client.</p>
9
9
 
10
10
  ```sh
11
11
  claude mcp add whiteboard --scope user -- npx -y @ansuman-shukla/whiteboard-mcp
@@ -23,7 +23,7 @@ claude mcp add whiteboard --scope user -- npx -y @ansuman-shukla/whiteboard-mcp
23
23
 
24
24
  <picture>
25
25
  <source media="(prefers-color-scheme: dark)" srcset="docs/media/readme/features-dark.svg">
26
- <img src="docs/media/readme/features-light.svg" width="100%" alt="Why whiteboard-mcp: draws step by step, sees what you draw, right beside the chat, remembers boards, local and private, tiny (one runtime dependency, about 7 KB of tool schema per turn).">
26
+ <img src="docs/media/readme/features-light.svg" width="100%" alt="Why Whiteboard MCP: draws step by step, sees what you draw, right beside the chat, remembers boards, local and private, tiny (one runtime dependency, about 7 KB of tool schema per turn).">
27
27
  </picture>
28
28
 
29
29
  ## What you can do with it
@@ -96,12 +96,12 @@ code --add-mcp '{"name":"whiteboard","command":"npx","args":["-y","@ansuman-shuk
96
96
 
97
97
  Agents without MCP, project-level configs and more: [client setup](docs/clients.md). For boards in a tab beside the chat, install the [IDE panel](docs/ide-panel.md). The bundled [skill](docs/clients.md#the-skill-recommended-for-every-agent) teaches the agent when a drawing helps.
98
98
 
99
- ## whiteboard-mcp vs Excalidraw MCP
99
+ ## Whiteboard MCP vs Excalidraw MCP
100
100
 
101
101
  <a href="docs/comparison.md">
102
102
  <picture>
103
103
  <source media="(prefers-color-scheme: dark)" srcset="docs/media/readme/compare-dark.svg">
104
- <img src="docs/media/readme/compare-light.svg" width="100%" alt="whiteboard-mcp vs Excalidraw MCP: whiteboard-mcp sees what you draw back as a short list of changes, screenshots its own board, edits one shape in place, keeps boards beyond the chat, works in any MCP client plus a CLI, and converts Mermaid. Excalidraw MCP has animated camera moves and zero install.">
104
+ <img src="docs/media/readme/compare-light.svg" width="100%" alt="Whiteboard MCP vs Excalidraw MCP: Whiteboard MCP sees what you draw back as a short list of changes, screenshots its own board, edits one shape in place, keeps boards beyond the chat, works in any MCP client plus a CLI, and converts Mermaid. Excalidraw MCP has animated camera moves and zero install.">
105
105
  </picture>
106
106
  </a>
107
107
 
@@ -111,7 +111,7 @@ Agents without MCP, project-level configs and more: [client setup](docs/clients.
111
111
 
112
112
  ### Is there an Excalidraw MCP server that runs locally and works offline?
113
113
 
114
- Yes, that's whiteboard-mcp. It runs a small daemon on `127.0.0.1` and serves the Excalidraw viewer with its fonts bundled. After the first `npx` download it needs no internet, no account and no API key. Boards are plain JSON files in `~/.whiteboard-mcp`.
114
+ Yes, that's Whiteboard MCP. It runs a small daemon on `127.0.0.1` and serves the Excalidraw viewer with its fonts bundled. After the first `npx` download it needs no internet, no account and no API key. Boards are plain JSON files in `~/.whiteboard-mcp`.
115
115
 
116
116
  ### How do I let Claude Code, Codex or Cursor draw diagrams?
117
117
 
@@ -123,7 +123,7 @@ Yes. Every time it looks, it gets a short list of what you added, moved or delet
123
123
 
124
124
  ### Which agents and editors does it work with?
125
125
 
126
- Any MCP client: Claude Code, Claude Desktop, OpenAI Codex, Cursor, Gemini CLI, VS Code (Copilot agent mode), Windsurf, Devin, OpenCode and more. Agents without MCP can use the plain `whiteboard-mcp call` CLI. The [IDE panel](docs/ide-panel.md) shows boards in a tab beside the chat in VS Code, Cursor, Windsurf and Devin Desktop; anywhere else they open in your browser.
126
+ Any MCP client: Claude Code, Claude Desktop, OpenAI Codex, Cursor, Gemini CLI, VS Code (Copilot agent mode), Windsurf, Devin, OpenCode and more. Agents without MCP can use the plain `whiteboard-mcp call` CLI. The [IDE panel](docs/ide-panel.md) shows boards in a tab beside the chat in VS Code, Cursor, Windsurf and Devin Desktop; elsewhere in an editor or desktop app, the agent opens them in the app's in-app browser, and in a plain terminal they open in your browser.
127
127
 
128
128
  ### Can it turn Mermaid into an Excalidraw diagram, or export it?
129
129
 
@@ -130,8 +130,8 @@ export class CanvasClient {
130
130
  return this.call('PUT', API.canvas(id), meta);
131
131
  }
132
132
  /** Asks the IDE panel host of the chat's window (`host`) or workspace to show the board. False when none qualifies. */
133
- async show(id, workspace, host) {
134
- const request = { workspace, ...(host ? { host } : {}) };
133
+ async show(id, workspace, host, wait = false) {
134
+ const request = { workspace, ...(host ? { host } : {}), ...(wait ? { wait } : {}) };
135
135
  try {
136
136
  await this.call('POST', API.show(id), request, 8000);
137
137
  return true;
@@ -197,7 +197,7 @@ export class CanvasClient {
197
197
  await this.call('DELETE', API.canvas(id));
198
198
  }
199
199
  viewerUrl(id) {
200
- return `${this.baseUrl}${API.viewer(id)}?token=${this.token}`;
200
+ return `${this.baseUrl}${API.viewer(id, this.token)}`;
201
201
  }
202
202
  async call(method, path, body, timeoutMs = HTTP_TIMEOUT_MS, retry = true) {
203
203
  let res;
@@ -6,7 +6,8 @@ import { extname, isAbsolute, join, relative, resolve, sep } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
  import { WebSocketServer, WebSocket } from 'ws';
8
8
  import { APP_NAME, VERSION, preferredPort, stateDir, statePath } from '../shared/config.js';
9
- import { API, CANVAS_ID_PATTERN, } from '../shared/protocol.js';
9
+ import { API, CANVAS_ID_PATTERN, parseViewerPath, } from '../shared/protocol.js';
10
+ import { loadPrefs, parsePrefs, savePrefs } from './prefs.js';
10
11
  import { SavedStore } from './saved.js';
11
12
  import { MAX_SEARCH_LIMIT } from './search.js';
12
13
  import { CanvasStore, NotFoundError, ValidationError, validateElements } from './store.js';
@@ -19,6 +20,19 @@ function contains(folder, target) {
19
20
  const rel = relative(folder, target);
20
21
  return rel === '' || (!!rel && !rel.startsWith('..') && !isAbsolute(rel));
21
22
  }
23
+ /**
24
+ * A page served from this machine on any port, so viewers behind a local preview proxy (an editor's
25
+ * in-app browser) can connect. The token still guards every socket; this only keeps remote sites out.
26
+ */
27
+ function isLoopbackOrigin(origin) {
28
+ try {
29
+ const url = new URL(origin);
30
+ return url.protocol === 'http:' && (url.hostname === '127.0.0.1' || url.hostname === 'localhost') && url.origin === origin;
31
+ }
32
+ catch {
33
+ return false;
34
+ }
35
+ }
22
36
  const MAX_BODY_BYTES = 30 * 1024 * 1024;
23
37
  const MAX_SOCKET_BUFFER = 64 * 1024 * 1024;
24
38
  const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
@@ -123,6 +137,16 @@ async function readJson(req) {
123
137
  throw new HttpError(400, 'body must be a JSON object');
124
138
  return body;
125
139
  }
140
+ const LEGACY_VIEWER = /^\/c\/([^/]+)(\/?)$/;
141
+ /** Older links (`/c/<id>?token=`) move to the self-contained viewer path, relative so a proxy prefix survives. */
142
+ function redirectLegacyViewer(res, url, [, id, slash]) {
143
+ const token = url.searchParams.get('token');
144
+ if (!token)
145
+ return sendText(res, 401, 'This board link has no token. Open the board from your agent.');
146
+ url.searchParams.delete('token');
147
+ res.writeHead(302, { Location: `${slash ? '' : `${id}/`}${encodeURIComponent(token)}/${url.search}`, 'Cache-Control': 'no-store', 'Referrer-Policy': 'no-referrer' });
148
+ res.end();
149
+ }
126
150
  function rejectUpgrade(socket, status) {
127
151
  // Destroying right after end() can drop the unflushed status line; wait for the write to finish.
128
152
  socket.once('finish', () => socket.destroy());
@@ -143,6 +167,8 @@ export async function startDaemon(opts = {}) {
143
167
  const log = opts.log ?? (() => { });
144
168
  const store = new CanvasStore({ dir: join(opts.dir ?? stateDir(), 'canvases'), onError: (err) => log(`flush failed: ${String(err)}`) });
145
169
  const saved = new SavedStore({ dir: join(opts.dir ?? stateDir(), 'saved') });
170
+ const prefsFile = statePath('prefs.json', opts.dir ?? stateDir());
171
+ let prefs = loadPrefs(prefsFile);
146
172
  const viewersByCanvas = new Map();
147
173
  const pending = new Map();
148
174
  const hosts = new Set();
@@ -152,15 +178,14 @@ export async function startDaemon(opts = {}) {
152
178
  const hostGraceMs = opts.hostGraceMs ?? DEFAULT_HOST_GRACE_MS;
153
179
  const timers = [];
154
180
  let allowedHosts = new Set();
155
- let allowedOrigins = new Set();
156
181
  let lastActivityAt = Date.now();
157
182
  let activitySeq = 0;
158
183
  let closing;
159
184
  const viewerCount = (canvasId) => viewersByCanvas.get(canvasId)?.size ?? 0;
185
+ const tokenMatches = (given) => typeof given === 'string' && timingSafeEqual(digest(given), tokenDigest);
160
186
  const authorized = (req, url) => {
161
187
  const header = req.headers.authorization;
162
- const given = header?.startsWith('Bearer ') ? header.slice(7) : url.searchParams.get('token');
163
- return typeof given === 'string' && timingSafeEqual(digest(given), tokenDigest);
188
+ return tokenMatches(header?.startsWith('Bearer ') ? header.slice(7) : url.searchParams.get('token'));
164
189
  };
165
190
  function send(viewer, msg) {
166
191
  if (viewer.ws.readyState !== WebSocket.OPEN)
@@ -262,14 +287,16 @@ export async function startDaemon(opts = {}) {
262
287
  }
263
288
  /**
264
289
  * The chat's own window (hostId equals the hint) wins. Otherwise a window whose folders contain the
265
- * board's workspace, focused first, then the most recent; a window known to be another one than the
266
- * hinted one is never used. No match means no host: showing a chat's board in an unrelated window
267
- * would hide that window's own board.
290
+ * board's workspace, focused first, then the most recent. While the hinted window may still connect
291
+ * (`strict`), other identified windows are skipped; once it can't (a stale id after Reload Window),
292
+ * the folder decides. No match means no host: a board never opens in a window of another folder.
268
293
  */
269
- function pickHost(workspace, hint) {
294
+ function pickHost(workspace, hint, strict) {
270
295
  const rank = (h) => {
271
- if (hint !== undefined && h.hostId !== undefined)
272
- return h.hostId === hint ? 3 : 0;
296
+ if (hint !== undefined && h.hostId === hint)
297
+ return 3;
298
+ if (strict && hint !== undefined && h.hostId !== undefined)
299
+ return 0;
273
300
  if (!workspace || !h.folders.some((f) => contains(f, workspace)))
274
301
  return 0;
275
302
  return h.focused ? 2 : 1;
@@ -285,24 +312,26 @@ export async function startDaemon(opts = {}) {
285
312
  }
286
313
  /**
287
314
  * Right after the daemon starts (the agent's first call started it), the chat's window hasn't found it
288
- * yet; a show that names its window waits briefly for that window's host instead of answering no_host.
315
+ * yet: a show that names its window, or comes from an agent inside an editor (`wait`), waits briefly
316
+ * for a host instead of answering no_host. A hinted show waits for its own window first.
289
317
  */
290
- function show(canvasId, workspace, hint, res) {
291
- const host = pickHost(workspace, hint);
318
+ function show(canvasId, workspace, hint, wait, res) {
319
+ const graceLeft = startedAt + hostGraceMs - Date.now();
320
+ const waiting = graceLeft > 0 && (hint !== undefined || wait);
321
+ const host = pickHost(workspace, hint, waiting);
292
322
  if (host)
293
323
  return showOn(host, canvasId, res);
294
- const graceLeft = startedAt + hostGraceMs - Date.now();
295
- if (hint === undefined || graceLeft <= 0)
324
+ if (!waiting)
296
325
  return Promise.resolve(sendJson(res, 409, { error: 'no_host' }));
297
326
  return new Promise((done) => {
298
327
  const end = () => {
299
- hostWaits.delete(wait);
328
+ hostWaits.delete(waiter);
300
329
  clearTimeout(timer);
301
330
  res.off('close', onClientGone);
302
331
  };
303
- const wait = {
332
+ const waiter = {
304
333
  retry: () => {
305
- const found = pickHost(workspace, hint);
334
+ const found = pickHost(workspace, hint, true);
306
335
  if (!found)
307
336
  return;
308
337
  end();
@@ -318,9 +347,15 @@ export async function startDaemon(opts = {}) {
318
347
  end();
319
348
  done();
320
349
  };
321
- const timer = setTimeout(() => wait.finish(409, { error: 'no_host' }), graceLeft);
350
+ const timer = setTimeout(() => {
351
+ const fallback = pickHost(workspace, hint, false);
352
+ if (!fallback)
353
+ return waiter.finish(409, { error: 'no_host' });
354
+ end();
355
+ void showOn(fallback, canvasId, res).then(done);
356
+ }, graceLeft);
322
357
  res.once('close', onClientGone);
323
- hostWaits.add(wait);
358
+ hostWaits.add(waiter);
324
359
  });
325
360
  }
326
361
  function showOn(host, canvasId, res) {
@@ -369,13 +404,15 @@ export async function startDaemon(opts = {}) {
369
404
  return sendJson(res, 200, { ...store.ensure(id, { title, workspace, chat }), viewers: viewerCount(id) });
370
405
  }
371
406
  case 'POST show': {
372
- const { workspace, host: hint } = await readJson(req);
407
+ const { workspace, host: hint, wait } = await readJson(req);
373
408
  if (workspace !== undefined && typeof workspace !== 'string')
374
409
  throw new HttpError(400, 'workspace must be a string');
410
+ if (wait !== undefined && typeof wait !== 'boolean')
411
+ throw new HttpError(400, 'wait must be a boolean');
375
412
  if (hint !== undefined && (typeof hint !== 'string' || !hint || hint.length > MAX_HOST_ID))
376
413
  throw new HttpError(400, 'host must be a non-empty string');
377
414
  requireCanvas();
378
- return show(id, workspace, hint, res);
415
+ return show(id, workspace, hint, wait === true, res);
379
416
  }
380
417
  case 'DELETE ':
381
418
  if (!store.remove(id))
@@ -437,7 +474,14 @@ export async function startDaemon(opts = {}) {
437
474
  async function serveStatic(req, res, url) {
438
475
  if (req.method !== 'GET' && req.method !== 'HEAD')
439
476
  return sendText(res, 405, 'method not allowed');
440
- const isViewer = /^\/c\/[^/]+\/?$/.test(url.pathname);
477
+ const legacy = LEGACY_VIEWER.exec(url.pathname);
478
+ if (legacy)
479
+ return redirectLegacyViewer(res, url, legacy);
480
+ const viewer = parseViewerPath(url.pathname);
481
+ if (viewer && !CANVAS_ID_PATTERN.test(viewer.canvasId))
482
+ return sendText(res, 404, 'not found');
483
+ const path = viewer ? `/${viewer.rest}` : url.pathname;
484
+ const isViewer = viewer?.rest === '';
441
485
  let file;
442
486
  if (isViewer) {
443
487
  file = join(frontendDir, 'index.html');
@@ -445,7 +489,7 @@ export async function startDaemon(opts = {}) {
445
489
  else {
446
490
  let rel;
447
491
  try {
448
- rel = decodeURIComponent(url.pathname);
492
+ rel = decodeURIComponent(path);
449
493
  }
450
494
  catch {
451
495
  return sendText(res, 400, 'bad path');
@@ -463,13 +507,15 @@ export async function startDaemon(opts = {}) {
463
507
  catch {
464
508
  return isViewer ? sendText(res, 503, 'frontend not built') : sendText(res, 404, 'not found');
465
509
  }
466
- const data = await readFile(real);
510
+ const content = await readFile(real);
511
+ // The page can't always read its board from its own URL (a proxy may mount it at '/'), so it's told here.
512
+ const data = viewer && isViewer ? Buffer.from(content.toString('utf8').replace('<head>', `<head><meta name="whiteboard-canvas" content="${viewer.canvasId}">`)) : content;
467
513
  if (res.destroyed)
468
514
  return;
469
515
  res.writeHead(200, {
470
516
  'Content-Type': MIME[extname(real).toLowerCase()] ?? 'application/octet-stream',
471
517
  'Content-Length': data.length,
472
- 'Cache-Control': url.pathname.startsWith('/assets/') ? 'public, max-age=31536000, immutable' : 'no-store',
518
+ 'Cache-Control': path.startsWith('/assets/') ? 'public, max-age=31536000, immutable' : 'no-store',
473
519
  'X-Content-Type-Options': 'nosniff',
474
520
  // The viewer URL carries the token in its query string.
475
521
  'Referrer-Policy': 'no-referrer',
@@ -563,6 +609,15 @@ export async function startDaemon(opts = {}) {
563
609
  throw new ValidationError('visible must be a boolean');
564
610
  viewer.visible = msg.visible;
565
611
  return;
612
+ case 'prefs': {
613
+ prefs = parsePrefs(msg.prefs);
614
+ savePrefs(prefsFile, prefs);
615
+ for (const set of viewersByCanvas.values())
616
+ for (const other of set)
617
+ if (other !== viewer)
618
+ send(other, { t: 'prefs', prefs });
619
+ return;
620
+ }
566
621
  default:
567
622
  throw new ValidationError(`unknown message type ${JSON.stringify(msg.t)}`);
568
623
  }
@@ -645,6 +700,7 @@ export async function startDaemon(opts = {}) {
645
700
  try {
646
701
  store.ensure(canvasId);
647
702
  send(viewer, { t: 'snapshot', canvas: store.snapshot(canvasId, viewers.size) });
703
+ send(viewer, { t: 'prefs', prefs });
648
704
  }
649
705
  catch (err) {
650
706
  log(`snapshot failed: ${String(err)}`);
@@ -660,17 +716,19 @@ export async function startDaemon(opts = {}) {
660
716
  const url = new URL(req.url ?? '/', 'http://localhost');
661
717
  if (closing)
662
718
  return rejectUpgrade(socket, 503);
663
- if (url.pathname !== '/ws' && url.pathname !== API.hostWs)
719
+ const viewerPath = parseViewerPath(url.pathname);
720
+ const viewerWs = viewerPath?.rest === 'ws' ? viewerPath : undefined;
721
+ if (url.pathname !== '/ws' && url.pathname !== API.hostWs && !viewerWs)
664
722
  return rejectUpgrade(socket, 404);
665
723
  if (!allowedHosts.has(req.headers.host ?? ''))
666
724
  return rejectUpgrade(socket, 403);
667
- if (req.headers.origin !== undefined && !allowedOrigins.has(req.headers.origin))
725
+ if (req.headers.origin !== undefined && !isLoopbackOrigin(req.headers.origin))
668
726
  return rejectUpgrade(socket, 403);
669
- if (!authorized(req, url))
727
+ if (!(viewerWs ? tokenMatches(viewerWs.token) : authorized(req, url)))
670
728
  return rejectUpgrade(socket, 401);
671
729
  if (url.pathname === API.hostWs)
672
730
  return wss.handleUpgrade(req, socket, head, onHost);
673
- const canvasId = url.searchParams.get('canvas') ?? '';
731
+ const canvasId = viewerWs?.canvasId ?? url.searchParams.get('canvas') ?? '';
674
732
  if (!CANVAS_ID_PATTERN.test(canvasId))
675
733
  return rejectUpgrade(socket, 400);
676
734
  wss.handleUpgrade(req, socket, head, (ws) => onConnection(ws, canvasId));
@@ -688,7 +746,6 @@ export async function startDaemon(opts = {}) {
688
746
  server.on('error', (err) => log(`server error: ${err.message}`));
689
747
  const port = server.address().port;
690
748
  allowedHosts = new Set([`127.0.0.1:${port}`, `localhost:${port}`]);
691
- allowedOrigins = new Set([`http://127.0.0.1:${port}`, `http://localhost:${port}`]);
692
749
  const every = (ms, fn) => {
693
750
  const t = setInterval(fn, ms);
694
751
  t.unref();
@@ -0,0 +1,25 @@
1
+ import { readFileSync, renameSync, writeFileSync } from 'node:fs';
2
+ import { THEME_CHOICES } from '../shared/protocol.js';
3
+ import { ValidationError } from './store.js';
4
+ export const DEFAULT_PREFS = { theme: 'system' };
5
+ const isObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
6
+ export function parsePrefs(input) {
7
+ const theme = isObject(input) ? input.theme : undefined;
8
+ if (!THEME_CHOICES.includes(theme))
9
+ throw new ValidationError(`prefs.theme must be one of ${THEME_CHOICES.join(', ')}`);
10
+ return { theme: theme };
11
+ }
12
+ /** The saved prefs; the defaults when the file is missing or unreadable, since they're only display settings. */
13
+ export function loadPrefs(file) {
14
+ try {
15
+ return parsePrefs(JSON.parse(readFileSync(file, 'utf8')));
16
+ }
17
+ catch {
18
+ return DEFAULT_PREFS;
19
+ }
20
+ }
21
+ export function savePrefs(file, prefs) {
22
+ const tmp = `${file}.tmp-${process.pid}`;
23
+ writeFileSync(tmp, JSON.stringify(prefs), { mode: 0o600 });
24
+ renameSync(tmp, file);
25
+ }
@@ -1 +1 @@
1
- import{g as p,r as u,d as a}from"./chunk-SVP7TREG-mpQRweZv.js";import{p as f}from"./chunk-JWPE2WC7-7Pq7jJ1J.js";import{_ as n,l as o}from"./index-Df2Luesx.js";import{M as c,b as d}from"./cynefin-OW5HDTMX-BQKgYdPD.js";import"./index-BDFEvJ3q.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
1
+ import{g as p,r as u,d as a}from"./chunk-SVP7TREG-DE2-zIGo.js";import{p as f}from"./chunk-JWPE2WC7-CME61VGI.js";import{_ as n,l as o}from"./index-B4ANUftc.js";import{M as c,b as d}from"./cynefin-OW5HDTMX-9qcvNUxp.js";import"./index-D1iAevn8.js";var v=d().RailroadAbnf.parser.LangiumParser,i=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),l=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:l,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return i(e.element);case"AbnfOptionalGroup":return{type:"optional",element:i(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:i(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},B={parser:R,db:a,renderer:u,styles:p};export{B as diagram};
@@ -1,4 +1,4 @@
1
- import"./index-BDFEvJ3q.js";var u={paste:"لصق",pasteAsPlaintext:"اللصق كنص عادي",pasteCharts:"لصق الرسوم البيانية",selectAll:"تحديد الكل",multiSelect:"إضافة عنصر للتحديد",moveCanvas:"نقل لوح الرسم",cut:"قص",copy:"نسخ",copyAsPng:"نسخ إلى الحافظة بصيغة PNG",copyAsSvg:"نسخ إلى الحافظة بصيغة SVG",copyText:"نسخ إلى الحافظة كنص",copySource:"",convertToCode:"",bringForward:"جلب للأمام",sendToBack:"أرسل للخلف",bringToFront:"أحضر للأمام",sendBackward:"أرسل للخلف",delete:"حذف",copyStyles:"نسخ الأنماط",pasteStyles:"لصق الأنماط",stroke:"الخط",background:"الخلفية",fill:"التعبئة",strokeWidth:"سُمك الخط",strokeStyle:"نمط الخط",strokeStyle_solid:"متصل",strokeStyle_dashed:"متقطع",strokeStyle_dotted:"منقط",sloppiness:"الإمالة",opacity:"الشفافية",textAlign:"محاذاة النص",edges:"الحواف",sharp:"حادة",round:"دائرية",arrowheads:"رؤوس الأسهم",arrowhead_none:"لا شيء",arrowhead_arrow:"سهم",arrowhead_bar:"شريط",arrowhead_circle:"",arrowhead_circle_outline:"",arrowhead_triangle:"مثلث",arrowhead_triangle_outline:"",arrowhead_diamond:"",arrowhead_diamond_outline:"",fontSize:"حجم الخط",fontFamily:"نوع الخط",addWatermark:'إضافة "مصنوعة بواسطة Excalidraw"',handDrawn:"رسم باليد",normal:"عادي",code:"رمز",small:"صغير",medium:"متوسط",large:"كبير",veryLarge:"كبير جدا",solid:"كامل",hachure:"خطوط",zigzag:"متعرج",crossHatch:"خطوط متقطعة",thin:"نحيف",bold:"داكن",left:"الـيسار",center:"وسط",right:"يمين",extraBold:"عريض",architect:"معماري",artist:"رسام",cartoonist:"كرتوني",fileTitle:"إسم الملف",colorPicker:"منتقي اللون",canvasColors:"تستخدم على القماش",canvasBackground:"خلفية اللوحة",drawingCanvas:"لوحة الرسم",layers:"الطبقات",actions:"الإجراءات",language:"اللغة",liveCollaboration:"التعاون المباشر...",duplicateSelection:"تكرار",untitled:"غير معنون",name:"الاسم",yourName:"اسمك",madeWithExcalidraw:"مصنوعة بواسطة Excalidraw",group:"تحديد مجموعة",ungroup:"إلغاء تحديد مجموعة",collaborators:"المتعاونون",showGrid:"إظهار الشبكة",addToLibrary:"أضف إلى المكتبة",removeFromLibrary:"حذف من المكتبة",libraryLoadingMessage:"جارٍ تحميل المكتبة…",libraries:"تصفح المكتبات",loadingScene:"جاري تحميل المشهد…",align:"محاذاة",alignTop:"محاذاة إلى اﻷعلى",alignBottom:"محاذاة إلى اﻷسفل",alignLeft:"محاذاة إلى اليسار",alignRight:"محاذاة إلى اليمين",centerVertically:"توسيط عمودي",centerHorizontally:"توسيط أفقي",distributeHorizontally:"التوزيع الأفقي",distributeVertically:"التوزيع عمودياً",flipHorizontal:"قلب عامودي",flipVertical:"قلب أفقي",viewMode:"نمط العرض",share:"مشاركة",showStroke:"إظهار منتقي لون الخط",showBackground:"إظهار منتقي لون الخلفية",toggleTheme:"غير النمط",personalLib:"المكتبة الشخصية",excalidrawLib:"مكتبتنا",decreaseFontSize:"تصغير حجم الخط",increaseFontSize:"تكبير حجم الخط",unbindText:"فك ربط النص",bindText:"ربط النص بالحاوية",createContainerFromText:"نص مغلف في حاوية",link:{edit:"تعديل الرابط",editEmbed:"تحرير الرابط وإدراجه",create:"إنشاء رابط",createEmbed:"إنشاء رابط و إدراجه",label:"رابط",labelEmbed:"رابط و إدراج",empty:"لم يتم تعيين رابط"},lineEditor:{edit:"تحرير السطر",exit:"الخروج من المُحرر"},elementLock:{lock:"قفل",unlock:"فتح",lockAll:"قفل الكل",unlockAll:"فتح الكل"},statusPublished:"نُشر",sidebarLock:"إبقاء الشريط الجانبي مفتوح",selectAllElementsInFrame:"تحديد جميع العناصر في الإطار",removeAllElementsFromFrame:"إزالة جميع العناصر من الإطار",eyeDropper:"اختيار اللون من القماش",textToDiagram:"",prompt:""},A={noItems:"لا توجد عناصر أضيفت بعد...",hint_emptyLibrary:"حدد عنصر على القماش لإضافته هنا، أو تثبيت مكتبة من المستودع العام أدناه.",hint_emptyPrivateLibrary:"حدد عنصر على القماش لإضافته هنا."},e={clearReset:"إعادة تعيين اللوحة",exportJSON:"صدر الملف",exportImage:"تصدير الصورة...",export:"حفظ إلى...",copyToClipboard:"نسخ إلى الحافظة",save:"احفظ للملف الحالي",saveAs:"حفظ كـ",load:"فتح",getShareableLink:"احصل على رابط المشاركة",close:"غلق",selectLanguage:"اختر اللغة",scrollBackToContent:"الرجوع إلى المحتوى",zoomIn:"تكبير",zoomOut:"تصغير",resetZoom:"إعادة تعيين الشاشة",menu:"القائمة",done:"تم",edit:"تعديل",undo:"تراجع",redo:"إعادة تنفيذ",resetLibrary:"إعادة ضبط المكتبة",createNewRoom:"إنشاء غرفة جديدة",fullScreen:"شاشة كاملة",darkMode:"الوضع المظلم",lightMode:"الوضع المضيء",zenMode:"وضع التأمل",objectsSnapMode:"التقط إلى العناصر",exitZenMode:"إلغاء الوضع الليلى",cancel:"إلغاء",clear:"مسح",remove:"إزالة",embed:"تبديل الإدراج",publishLibrary:"انشر",submit:"أرسل",confirm:"تأكيد",embeddableInteractionButton:"اضغط للتفاعل"},a={clearReset:"هذا سيُزيل كامل اللوحة. هل أنت متأكد؟",couldNotCreateShareableLink:"تعذر إنشاء رابطة المشاركة.",couldNotCreateShareableLinkTooBig:"تعذر إنشاء رابط قابل للمشاركة: المشهد كبير جدًا",couldNotLoadInvalidFile:"تعذر التحميل، الملف غير صالح",importBackendFailed:"فشل الاستيراد من الخادوم.",cannotExportEmptyCanvas:"لا يمكن تصدير لوحة فارغة.",couldNotCopyToClipboard:"تعذر النسخ إلى الحافظة.",decryptFailed:"تعذر فك تشفير البيانات.",uploadedSecurly:"تم تأمين التحميل بتشفير النهاية إلى النهاية، مما يعني أن خادوم Excalidraw والأطراف الثالثة لا يمكنها قراءة المحتوى.",loadSceneOverridePrompt:"تحميل الرسم الخارجي سيحل محل المحتوى الموجود لديك. هل ترغب في المتابعة؟",collabStopOverridePrompt:`إيقاف الجلسة سيؤدي إلى الكتابة فوق رسومك السابقة المخزنة داخليا. هل أنت متأكد؟
1
+ import"./index-D1iAevn8.js";var u={paste:"لصق",pasteAsPlaintext:"اللصق كنص عادي",pasteCharts:"لصق الرسوم البيانية",selectAll:"تحديد الكل",multiSelect:"إضافة عنصر للتحديد",moveCanvas:"نقل لوح الرسم",cut:"قص",copy:"نسخ",copyAsPng:"نسخ إلى الحافظة بصيغة PNG",copyAsSvg:"نسخ إلى الحافظة بصيغة SVG",copyText:"نسخ إلى الحافظة كنص",copySource:"",convertToCode:"",bringForward:"جلب للأمام",sendToBack:"أرسل للخلف",bringToFront:"أحضر للأمام",sendBackward:"أرسل للخلف",delete:"حذف",copyStyles:"نسخ الأنماط",pasteStyles:"لصق الأنماط",stroke:"الخط",background:"الخلفية",fill:"التعبئة",strokeWidth:"سُمك الخط",strokeStyle:"نمط الخط",strokeStyle_solid:"متصل",strokeStyle_dashed:"متقطع",strokeStyle_dotted:"منقط",sloppiness:"الإمالة",opacity:"الشفافية",textAlign:"محاذاة النص",edges:"الحواف",sharp:"حادة",round:"دائرية",arrowheads:"رؤوس الأسهم",arrowhead_none:"لا شيء",arrowhead_arrow:"سهم",arrowhead_bar:"شريط",arrowhead_circle:"",arrowhead_circle_outline:"",arrowhead_triangle:"مثلث",arrowhead_triangle_outline:"",arrowhead_diamond:"",arrowhead_diamond_outline:"",fontSize:"حجم الخط",fontFamily:"نوع الخط",addWatermark:'إضافة "مصنوعة بواسطة Excalidraw"',handDrawn:"رسم باليد",normal:"عادي",code:"رمز",small:"صغير",medium:"متوسط",large:"كبير",veryLarge:"كبير جدا",solid:"كامل",hachure:"خطوط",zigzag:"متعرج",crossHatch:"خطوط متقطعة",thin:"نحيف",bold:"داكن",left:"الـيسار",center:"وسط",right:"يمين",extraBold:"عريض",architect:"معماري",artist:"رسام",cartoonist:"كرتوني",fileTitle:"إسم الملف",colorPicker:"منتقي اللون",canvasColors:"تستخدم على القماش",canvasBackground:"خلفية اللوحة",drawingCanvas:"لوحة الرسم",layers:"الطبقات",actions:"الإجراءات",language:"اللغة",liveCollaboration:"التعاون المباشر...",duplicateSelection:"تكرار",untitled:"غير معنون",name:"الاسم",yourName:"اسمك",madeWithExcalidraw:"مصنوعة بواسطة Excalidraw",group:"تحديد مجموعة",ungroup:"إلغاء تحديد مجموعة",collaborators:"المتعاونون",showGrid:"إظهار الشبكة",addToLibrary:"أضف إلى المكتبة",removeFromLibrary:"حذف من المكتبة",libraryLoadingMessage:"جارٍ تحميل المكتبة…",libraries:"تصفح المكتبات",loadingScene:"جاري تحميل المشهد…",align:"محاذاة",alignTop:"محاذاة إلى اﻷعلى",alignBottom:"محاذاة إلى اﻷسفل",alignLeft:"محاذاة إلى اليسار",alignRight:"محاذاة إلى اليمين",centerVertically:"توسيط عمودي",centerHorizontally:"توسيط أفقي",distributeHorizontally:"التوزيع الأفقي",distributeVertically:"التوزيع عمودياً",flipHorizontal:"قلب عامودي",flipVertical:"قلب أفقي",viewMode:"نمط العرض",share:"مشاركة",showStroke:"إظهار منتقي لون الخط",showBackground:"إظهار منتقي لون الخلفية",toggleTheme:"غير النمط",personalLib:"المكتبة الشخصية",excalidrawLib:"مكتبتنا",decreaseFontSize:"تصغير حجم الخط",increaseFontSize:"تكبير حجم الخط",unbindText:"فك ربط النص",bindText:"ربط النص بالحاوية",createContainerFromText:"نص مغلف في حاوية",link:{edit:"تعديل الرابط",editEmbed:"تحرير الرابط وإدراجه",create:"إنشاء رابط",createEmbed:"إنشاء رابط و إدراجه",label:"رابط",labelEmbed:"رابط و إدراج",empty:"لم يتم تعيين رابط"},lineEditor:{edit:"تحرير السطر",exit:"الخروج من المُحرر"},elementLock:{lock:"قفل",unlock:"فتح",lockAll:"قفل الكل",unlockAll:"فتح الكل"},statusPublished:"نُشر",sidebarLock:"إبقاء الشريط الجانبي مفتوح",selectAllElementsInFrame:"تحديد جميع العناصر في الإطار",removeAllElementsFromFrame:"إزالة جميع العناصر من الإطار",eyeDropper:"اختيار اللون من القماش",textToDiagram:"",prompt:""},A={noItems:"لا توجد عناصر أضيفت بعد...",hint_emptyLibrary:"حدد عنصر على القماش لإضافته هنا، أو تثبيت مكتبة من المستودع العام أدناه.",hint_emptyPrivateLibrary:"حدد عنصر على القماش لإضافته هنا."},e={clearReset:"إعادة تعيين اللوحة",exportJSON:"صدر الملف",exportImage:"تصدير الصورة...",export:"حفظ إلى...",copyToClipboard:"نسخ إلى الحافظة",save:"احفظ للملف الحالي",saveAs:"حفظ كـ",load:"فتح",getShareableLink:"احصل على رابط المشاركة",close:"غلق",selectLanguage:"اختر اللغة",scrollBackToContent:"الرجوع إلى المحتوى",zoomIn:"تكبير",zoomOut:"تصغير",resetZoom:"إعادة تعيين الشاشة",menu:"القائمة",done:"تم",edit:"تعديل",undo:"تراجع",redo:"إعادة تنفيذ",resetLibrary:"إعادة ضبط المكتبة",createNewRoom:"إنشاء غرفة جديدة",fullScreen:"شاشة كاملة",darkMode:"الوضع المظلم",lightMode:"الوضع المضيء",zenMode:"وضع التأمل",objectsSnapMode:"التقط إلى العناصر",exitZenMode:"إلغاء الوضع الليلى",cancel:"إلغاء",clear:"مسح",remove:"إزالة",embed:"تبديل الإدراج",publishLibrary:"انشر",submit:"أرسل",confirm:"تأكيد",embeddableInteractionButton:"اضغط للتفاعل"},a={clearReset:"هذا سيُزيل كامل اللوحة. هل أنت متأكد؟",couldNotCreateShareableLink:"تعذر إنشاء رابطة المشاركة.",couldNotCreateShareableLinkTooBig:"تعذر إنشاء رابط قابل للمشاركة: المشهد كبير جدًا",couldNotLoadInvalidFile:"تعذر التحميل، الملف غير صالح",importBackendFailed:"فشل الاستيراد من الخادوم.",cannotExportEmptyCanvas:"لا يمكن تصدير لوحة فارغة.",couldNotCopyToClipboard:"تعذر النسخ إلى الحافظة.",decryptFailed:"تعذر فك تشفير البيانات.",uploadedSecurly:"تم تأمين التحميل بتشفير النهاية إلى النهاية، مما يعني أن خادوم Excalidraw والأطراف الثالثة لا يمكنها قراءة المحتوى.",loadSceneOverridePrompt:"تحميل الرسم الخارجي سيحل محل المحتوى الموجود لديك. هل ترغب في المتابعة؟",collabStopOverridePrompt:`إيقاف الجلسة سيؤدي إلى الكتابة فوق رسومك السابقة المخزنة داخليا. هل أنت متأكد؟
2
2
 
3
3
  (إذا كنت ترغب في الاحتفاظ برسمك المخزن داخليا، ببساطة أغلق علامة تبويب المتصفح بدلاً من ذلك.)`,errorAddingToLibrary:"تعذر إضافة العنصر للمكتبة",errorRemovingFromLibrary:"تعذر إزالة العنصر من المكتبة",confirmAddLibrary:"هذا سيضيف {{numShapes}} شكل إلى مكتبتك. هل أنت متأكد؟",imageDoesNotContainScene:"يبدو أن هذه الصورة لا تحتوي على أي بيانات مشهد. هل قمت بتمكين تضمين المشهد أثناء التصدير؟",cannotRestoreFromImage:"تعذر استعادة المشهد من ملف الصورة",invalidSceneUrl:"تعذر استيراد المشهد من عنوان URL المتوفر. إما أنها مشوهة، أو لا تحتوي على بيانات Excalidraw JSON صالحة.",resetLibrary:"هذا سوف يمسح مكتبتك. هل أنت متأكد؟",removeItemsFromsLibrary:"حذف {{count}} عنصر (عناصر) من المكتبة؟",invalidEncryptionKey:"مفتاح التشفير يجب أن يكون من 22 حرفاً. التعاون المباشر معطل.",collabOfflineWarning:`لا يوجد اتصال بالانترنت.
4
4
  لن يتم حفظ التغييرات التي قمت بها!`},r={unsupportedFileType:"نوع الملف غير مدعوم.",imageInsertError:"تعذر إدراج الصورة. حاول مرة أخرى لاحقاً...",fileTooBig:"الملف كبير جداً. الحد الأقصى المسموح به للحجم هو {{maxSize}}.",svgImageInsertError:"تعذر إدراج صورة SVG. يبدو أن ترميز SVG غير صحيح.",failedToFetchImage:"",invalidSVGString:"SVG غير صالح.",cannotResolveCollabServer:"تعذر الاتصال بخادم التعاون. الرجاء إعادة تحميل الصفحة والمحاولة مرة أخرى.",importLibraryError:"تعذر تحميل المكتبة",collabSaveFailed:"تعذر الحفظ في قاعدة البيانات. إذا استمرت المشاكل، يفضل أن تحفظ ملفك محليا كي لا تفقد عملك.",collabSaveFailed_sizeExceeded:"تعذر الحفظ في قاعدة البيانات، يبدو أن القماش كبير للغاية، يفضّل حفظ الملف محليا كي لا تفقد عملك.",imageToolNotSupported:"",brave_measure_text_error:{line1:"يبدو أنك تستخدم متصفح Brave مع إعداد <bold>حظر صارم لتتبع البصمة</bold>.",line2:"قد يؤدي هذا إلى كسر <bold>عناصر النص</bold> في الرسومات الخاصة بك.",line3:"من المستحسن إلغاء تفعيل هذا الإعداد. يمكنك اتباع <link>هذه الخطوات</link> لفعل ذلك.",line4:"إذا لم يصلح تعطيل هذا الإعداد طريقة عرض النصوص، الرجاء كتابة <issueLink>بلاغ</issueLink> على حسابنا في GitHub، أو راسلنا على <discordLink>Discord</discordLink>"},libraryElementTypeError:{embeddable:"لا يمكن إضافة العناصر القابلة للتضمين في المكتبة.",iframe:"",image:"سوف يتم دعم إضافة صور إلى المكتبة قريباً!"},asyncPasteFailedOnRead:"",asyncPasteFailedOnParse:"",copyToSystemClipboardFailed:""},o={selection:"تحديد",image:"إدراج صورة",rectangle:"مستطيل",diamond:"مضلع",ellipse:"دائرة",arrow:"سهم",line:"خط",freedraw:"رسم",text:"نص",library:"مكتبة",lock:"الحفاظ على أداة التحديد نشطة بعد الرسم",penMode:"وضع القلم - امنع اللمس",link:"إضافة/تحديث الرابط للشكل المحدد",eraser:"ممحاة",frame:"أداة الإطار",magicframe:"",embeddable:"تضمين ويب",laser:"مؤشر ليزر",hand:"يد (أداة الإزاحة)",extraTools:"المزيد من أﻷدوات",mermaidToExcalidraw:"",magicSettings:""},t={canvasActions:"إجراءات اللوحة",selectedShapeActions:"إجراءات الشكل المحدد",shapes:"الأشكال"},i={canvasPanning:"لتحريك القماش، اضغط على عجلة الفأرة أو مفتاح المسافة أثناء السحب، أو استخدم أداة اليد",linearElement:"انقر لبدء نقاط متعددة، اسحب لخط واحد",freeDraw:"انقر واسحب، افرج عند الانتهاء",text:"نصيحة: يمكنك أيضًا إضافة نص بالنقر المزدوج في أي مكان بأداة الاختيار",embeddable:"اضغط مع السحب لإنشاء موقع ويب مضمّن",text_selected:"انقر نقراً مزدوجاً أو اضغط ادخال لتعديل النص",text_editing:"اضغط على Esc أو (Ctrl أو Cmd) + Enter لإنهاء التعديل",linearElementMulti:"انقر فوق النقطة الأخيرة أو اضغط على Esc أو Enter للإنهاء",lockAngle:"يمكنك تقييد الزاوية بالضغط على SHIFT",resize:`يمكنك تقييد النسب بالضغط على SHIFT أثناء تغيير الحجم،
@@ -1 +1 @@
1
- import{H as ln,I as un,J as y,K as tn,L as Q,M as I,N as _,O as an,P as rn,Q as j,R as o,S as K,T as sn,V as on,W as fn}from"./index-Df2Luesx.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,q,O,v,R,L,u){var D=q-l,i=O-h,n=L-v,d=u-R,a=d*D-n*i;if(!(a*a<y))return a=(n*(h-R)-d*(l-v))/a,[l+a*D,h+a*i]}function U(l,h,q,O,v,R,L){var u=l-q,D=h-O,i=(L?R:-R)/j(u*u+D*D),n=i*D,d=-i*u,a=l+n,s=h+d,f=q+n,c=O+d,M=(a+f)/2,t=(s+c)/2,m=f-a,g=c-s,A=m*m+g*g,T=v-R,P=a*c-f*s,E=(g<0?-1:1)*j(fn(0,T*T*A-P*P)),H=(P*g-m*E)/A,J=(-P*m-g*E)/A,w=(P*g+m*E)/A,p=(-P*m+g*E)/A,x=H-M,e=J-t,r=w-M,N=p-t;return x*x+e*e>r*r+N*N&&(H=w,J=p),{cx:H,cy:J,x01:-n,y01:-d,x11:H*(v/T-1),y11:J*(v/T-1)}}function hn(){var l=cn,h=yn,q=K(0),O=null,v=gn,R=dn,L=mn,u=null,D=ln(i);function i(){var n,d,a=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=R.apply(this,arguments)-un,M=an(c-f),t=c>f;if(u||(u=n=D()),s<a&&(d=s,s=a,a=d),!(s>y))u.moveTo(0,0);else if(M>tn-y)u.moveTo(s*Q(f),s*I(f)),u.arc(0,0,s,f,c,!t),a>y&&(u.moveTo(a*Q(c),a*I(c)),u.arc(0,0,a,c,f,t));else{var m=f,g=c,A=f,T=c,P=M,E=M,H=L.apply(this,arguments)/2,J=H>y&&(O?+O.apply(this,arguments):j(a*a+s*s)),w=_(an(s-a)/2,+q.apply(this,arguments)),p=w,x=w,e,r;if(J>y){var N=sn(J/a*I(H)),z=sn(J/s*I(H));(P-=N*2)>y?(N*=t?1:-1,A+=N,T-=N):(P=0,A=T=(f+c)/2),(E-=z*2)>y?(z*=t?1:-1,m+=z,g-=z):(E=0,m=g=(f+c)/2)}var V=s*Q(m),W=s*I(m),B=a*Q(T),C=a*I(T);if(w>y){var F=s*Q(g),G=s*I(g),X=a*Q(A),Y=a*I(A),S;if(M<rn)if(S=pn(V,W,X,Y,F,G,B,C)){var Z=V-S[0],$=W-S[1],k=F-S[0],b=G-S[1],nn=1/I(on((Z*k+$*b)/(j(Z*Z+$*$)*j(k*k+b*b)))/2),en=j(S[0]*S[0]+S[1]*S[1]);p=_(w,(a-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}E>y?x>y?(e=U(X,Y,V,W,s,x,t),r=U(F,G,B,C,s,x,t),u.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?u.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(u.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),u.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),u.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(u.moveTo(V,W),u.arc(0,0,s,m,g,!t)):u.moveTo(V,W),!(a>y)||!(P>y)?u.lineTo(B,C):p>y?(e=U(B,C,F,G,a,-p,t),r=U(V,W,X,Y,a,-p,t),u.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?u.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(u.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),u.arc(0,0,a,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),u.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):u.arc(0,0,a,T,A,t)}if(u.closePath(),n)return u=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +R.apply(this,arguments))/2-rn/2;return[Q(d)*n,I(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:K(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:K(+n),i):h},i.cornerRadius=function(n){return arguments.length?(q=typeof n=="function"?n:K(+n),i):q},i.padRadius=function(n){return arguments.length?(O=n==null?null:typeof n=="function"?n:K(+n),i):O},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:K(+n),i):v},i.endAngle=function(n){return arguments.length?(R=typeof n=="function"?n:K(+n),i):R},i.padAngle=function(n){return arguments.length?(L=typeof n=="function"?n:K(+n),i):L},i.context=function(n){return arguments.length?(u=n??null,i):u},i}export{hn as d};
1
+ import{H as ln,I as un,J as y,K as tn,L as Q,M as I,N as _,O as an,P as rn,Q as j,R as o,S as K,T as sn,V as on,W as fn}from"./index-B4ANUftc.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,q,O,v,R,L,u){var D=q-l,i=O-h,n=L-v,d=u-R,a=d*D-n*i;if(!(a*a<y))return a=(n*(h-R)-d*(l-v))/a,[l+a*D,h+a*i]}function U(l,h,q,O,v,R,L){var u=l-q,D=h-O,i=(L?R:-R)/j(u*u+D*D),n=i*D,d=-i*u,a=l+n,s=h+d,f=q+n,c=O+d,M=(a+f)/2,t=(s+c)/2,m=f-a,g=c-s,A=m*m+g*g,T=v-R,P=a*c-f*s,E=(g<0?-1:1)*j(fn(0,T*T*A-P*P)),H=(P*g-m*E)/A,J=(-P*m-g*E)/A,w=(P*g+m*E)/A,p=(-P*m+g*E)/A,x=H-M,e=J-t,r=w-M,N=p-t;return x*x+e*e>r*r+N*N&&(H=w,J=p),{cx:H,cy:J,x01:-n,y01:-d,x11:H*(v/T-1),y11:J*(v/T-1)}}function hn(){var l=cn,h=yn,q=K(0),O=null,v=gn,R=dn,L=mn,u=null,D=ln(i);function i(){var n,d,a=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=R.apply(this,arguments)-un,M=an(c-f),t=c>f;if(u||(u=n=D()),s<a&&(d=s,s=a,a=d),!(s>y))u.moveTo(0,0);else if(M>tn-y)u.moveTo(s*Q(f),s*I(f)),u.arc(0,0,s,f,c,!t),a>y&&(u.moveTo(a*Q(c),a*I(c)),u.arc(0,0,a,c,f,t));else{var m=f,g=c,A=f,T=c,P=M,E=M,H=L.apply(this,arguments)/2,J=H>y&&(O?+O.apply(this,arguments):j(a*a+s*s)),w=_(an(s-a)/2,+q.apply(this,arguments)),p=w,x=w,e,r;if(J>y){var N=sn(J/a*I(H)),z=sn(J/s*I(H));(P-=N*2)>y?(N*=t?1:-1,A+=N,T-=N):(P=0,A=T=(f+c)/2),(E-=z*2)>y?(z*=t?1:-1,m+=z,g-=z):(E=0,m=g=(f+c)/2)}var V=s*Q(m),W=s*I(m),B=a*Q(T),C=a*I(T);if(w>y){var F=s*Q(g),G=s*I(g),X=a*Q(A),Y=a*I(A),S;if(M<rn)if(S=pn(V,W,X,Y,F,G,B,C)){var Z=V-S[0],$=W-S[1],k=F-S[0],b=G-S[1],nn=1/I(on((Z*k+$*b)/(j(Z*Z+$*$)*j(k*k+b*b)))/2),en=j(S[0]*S[0]+S[1]*S[1]);p=_(w,(a-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}E>y?x>y?(e=U(X,Y,V,W,s,x,t),r=U(F,G,B,C,s,x,t),u.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?u.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(u.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),u.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),u.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(u.moveTo(V,W),u.arc(0,0,s,m,g,!t)):u.moveTo(V,W),!(a>y)||!(P>y)?u.lineTo(B,C):p>y?(e=U(B,C,F,G,a,-p,t),r=U(V,W,X,Y,a,-p,t),u.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?u.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(u.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),u.arc(0,0,a,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),u.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):u.arc(0,0,a,T,A,t)}if(u.closePath(),n)return u=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +R.apply(this,arguments))/2-rn/2;return[Q(d)*n,I(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:K(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:K(+n),i):h},i.cornerRadius=function(n){return arguments.length?(q=typeof n=="function"?n:K(+n),i):q},i.padRadius=function(n){return arguments.length?(O=n==null?null:typeof n=="function"?n:K(+n),i):O},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:K(+n),i):v},i.endAngle=function(n){return arguments.length?(R=typeof n=="function"?n:K(+n),i):R},i.padAngle=function(n){return arguments.length?(L=typeof n=="function"?n:K(+n),i):L},i.context=function(n){return arguments.length?(u=n??null,i):u},i}export{hn as d};