@ryan_nookpi/pi-skill-excalidraw 0.1.2 → 0.1.4

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 (158) hide show
  1. package/README.md +20 -5
  2. package/package.json +8 -2
  3. package/skills/excalidraw/SKILL.md +19 -6
  4. package/skills/excalidraw/app/dist/assets/{abnfDiagram-VCTEODGH-Y56Pk8sn.js → abnfDiagram-VCTEODGH-CZIuQcX5.js} +1 -1
  5. package/skills/excalidraw/app/dist/assets/{ar-SA-G6X2FPQ2-DatABGjX.js → ar-SA-G6X2FPQ2-e2g2uVLm.js} +1 -1
  6. package/skills/excalidraw/app/dist/assets/architecture-7GRP2DOG-7jeFDpJR.js +1 -0
  7. package/skills/excalidraw/app/dist/assets/{architectureDiagram-5GKGNRK7-BkBTZ8aU.js → architectureDiagram-5GKGNRK7-JZq1kTdB.js} +1 -1
  8. package/skills/excalidraw/app/dist/assets/{az-AZ-76LH7QW2-WQrHOob7.js → az-AZ-76LH7QW2-C6UtibTN.js} +1 -1
  9. package/skills/excalidraw/app/dist/assets/{bg-BG-XCXSNQG7-BVvnFVRX.js → bg-BG-XCXSNQG7-xGOrRa6w.js} +1 -1
  10. package/skills/excalidraw/app/dist/assets/{blockDiagram-I7D4REHJ-D7KnLVtF.js → blockDiagram-I7D4REHJ-Do9SvsPC.js} +1 -1
  11. package/skills/excalidraw/app/dist/assets/{bn-BD-2XOGV67Q-DzlalUE2.js → bn-BD-2XOGV67Q-96ofOCIB.js} +1 -1
  12. package/skills/excalidraw/app/dist/assets/{c4Diagram-7LVT6UL2-nV-MvTJT.js → c4Diagram-7LVT6UL2-5ue2hi5B.js} +1 -1
  13. package/skills/excalidraw/app/dist/assets/{ca-ES-6MX7JW3Y-ILmKOQy8.js → ca-ES-6MX7JW3Y-DoJHtTpz.js} +1 -1
  14. package/skills/excalidraw/app/dist/assets/channel-uL0FsxtS.js +1 -0
  15. package/skills/excalidraw/app/dist/assets/{chunk-4HAMMTFA-CZal2Pyz.js → chunk-4HAMMTFA-BJl8f4hj.js} +1 -1
  16. package/skills/excalidraw/app/dist/assets/{chunk-75Z2AOVW-DVqFChOg.js → chunk-75Z2AOVW-BEZtmt_5.js} +1 -1
  17. package/skills/excalidraw/app/dist/assets/{chunk-DU6HZSFF-BavxyZSY.js → chunk-DU6HZSFF-BbRzTxIu.js} +1 -1
  18. package/skills/excalidraw/app/dist/assets/{chunk-EIO257PC-LlbbXIq6.js → chunk-EIO257PC-CDf-LIGO.js} +1 -1
  19. package/skills/excalidraw/app/dist/assets/{chunk-F27PBJKO-BWDl5_Sz.js → chunk-F27PBJKO-MV5PD49O.js} +1 -1
  20. package/skills/excalidraw/app/dist/assets/{chunk-GMAD6QVW-Rjz_G1RQ.js → chunk-GMAD6QVW-u83hsId7.js} +1 -1
  21. package/skills/excalidraw/app/dist/assets/{chunk-GVQU2GXP-BjxGnl2S.js → chunk-GVQU2GXP-DhRGdiur.js} +1 -1
  22. package/skills/excalidraw/app/dist/assets/{chunk-IMKFNOWR-BJ1FymPn.js → chunk-IMKFNOWR-LcKd9v9s.js} +1 -1
  23. package/skills/excalidraw/app/dist/assets/{chunk-L3NEJ4N5-sJejB3eG.js → chunk-L3NEJ4N5-cDOiUn0I.js} +1 -1
  24. package/skills/excalidraw/app/dist/assets/{chunk-OSK3NFVY-DLz3HNrx.js → chunk-OSK3NFVY-GcK4QREf.js} +1 -1
  25. package/skills/excalidraw/app/dist/assets/{chunk-P2QGCYS3-Dhy4aMJl.js → chunk-P2QGCYS3-Ch37Dd6P.js} +1 -1
  26. package/skills/excalidraw/app/dist/assets/{chunk-POPQ4Y6H-DOEx_tr4.js → chunk-POPQ4Y6H-ChWUKdmB.js} +1 -1
  27. package/skills/excalidraw/app/dist/assets/{chunk-PWAF6VOD-Bq0yNT01.js → chunk-PWAF6VOD-CW5GCk0X.js} +1 -1
  28. package/skills/excalidraw/app/dist/assets/{chunk-SHT3W25Y-CICAAbIL.js → chunk-SHT3W25Y-DffgJ0n3.js} +1 -1
  29. package/skills/excalidraw/app/dist/assets/{chunk-SVP7TREG-f6pYNHg0.js → chunk-SVP7TREG-BNSeHPRk.js} +1 -1
  30. package/skills/excalidraw/app/dist/assets/{chunk-TICWLB2K-DdRa5lMo.js → chunk-TICWLB2K-C6rdEwMp.js} +1 -1
  31. package/skills/excalidraw/app/dist/assets/classDiagram-ZZMXUADV-CuOnbBpH.js +1 -0
  32. package/skills/excalidraw/app/dist/assets/classDiagram-v2-VYDZK3BY-CuOnbBpH.js +1 -0
  33. package/skills/excalidraw/app/dist/assets/{cs-CZ-2BRQDIVT-tV1CvCxu.js → cs-CZ-2BRQDIVT-BE0Wk0Du.js} +1 -1
  34. package/skills/excalidraw/app/dist/assets/{cynefin-OW5HDTMX-D-XvS_F8.js → cynefin-OW5HDTMX-TLJoQn41.js} +1 -1
  35. package/skills/excalidraw/app/dist/assets/{cynefinDiagram-5FMLGOSQ-DWgEonYW.js → cynefinDiagram-5FMLGOSQ-rC-HSjVV.js} +1 -1
  36. package/skills/excalidraw/app/dist/assets/{da-DK-5WZEPLOC-zceHT2GU.js → da-DK-5WZEPLOC-Cs3oblPs.js} +1 -1
  37. package/skills/excalidraw/app/dist/assets/{dagre-GXQ25YYZ-d3UPf-z-.js → dagre-GXQ25YYZ-wyUKclSr.js} +1 -1
  38. package/skills/excalidraw/app/dist/assets/{de-DE-XR44H4JA-U1KbS0X3.js → de-DE-XR44H4JA-u3VqZl5o.js} +1 -1
  39. package/skills/excalidraw/app/dist/assets/{diagram-S7CK7UJ4-CRc82Fb9.js → diagram-S7CK7UJ4-BA0zr9Ln.js} +1 -1
  40. package/skills/excalidraw/app/dist/assets/{diagram-UQ7AKVKN-CgfviMBL.js → diagram-UQ7AKVKN-BrmvEPwN.js} +1 -1
  41. package/skills/excalidraw/app/dist/assets/{diagram-VSXAHHWV-Btexo3l8.js → diagram-VSXAHHWV-D_79GLkd.js} +1 -1
  42. package/skills/excalidraw/app/dist/assets/{diagram-VX7I27RA-DoMnip1P.js → diagram-VX7I27RA-D6MiLZH3.js} +1 -1
  43. package/skills/excalidraw/app/dist/assets/{diagram-Z3DM3KII-BTsxq82z.js → diagram-Z3DM3KII-DUBocNEy.js} +1 -1
  44. package/skills/excalidraw/app/dist/assets/{dist-DVyo1T_H.js → dist-CZtRAIh5.js} +4 -4
  45. package/skills/excalidraw/app/dist/assets/{ebnfDiagram-PWID7BFC-BYsNxk9n.js → ebnfDiagram-PWID7BFC-Bok_2JTv.js} +1 -1
  46. package/skills/excalidraw/app/dist/assets/{el-GR-BZB4AONW-CgSV8rfE.js → el-GR-BZB4AONW-C8dhXUCR.js} +1 -1
  47. package/skills/excalidraw/app/dist/assets/{en-B4ZKOASM-Byd7DbO_.js → en-B4ZKOASM-CuIv77eQ.js} +1 -1
  48. package/skills/excalidraw/app/dist/assets/{erDiagram-RLTQ6QDP-LkewsTFZ.js → erDiagram-RLTQ6QDP-DmZZemzY.js} +1 -1
  49. package/skills/excalidraw/app/dist/assets/{es-ES-U4NZUMDT-d42UjG1W.js → es-ES-U4NZUMDT-DUMCK6Ck.js} +1 -1
  50. package/skills/excalidraw/app/dist/assets/{eu-ES-A7QVB2H4-CeMeFdnp.js → eu-ES-A7QVB2H4-CVLkKQxE.js} +1 -1
  51. package/skills/excalidraw/app/dist/assets/eventmodeling-NTZA5JFV-zQWNUpTz.js +1 -0
  52. package/skills/excalidraw/app/dist/assets/{fa-IR-HGAKTJCU-CVXIrtPl.js → fa-IR-HGAKTJCU-DGiyThKe.js} +1 -1
  53. package/skills/excalidraw/app/dist/assets/{fi-FI-Z5N7JZ37-DIW6dubX.js → fi-FI-Z5N7JZ37-D4go03qO.js} +1 -1
  54. package/skills/excalidraw/app/dist/assets/flowDiagram-HODETNUW-CnMHGSoA.js +1 -0
  55. package/skills/excalidraw/app/dist/assets/{fr-FR-RHASNOE6-EPyEt_4g.js → fr-FR-RHASNOE6-D87DQ39u.js} +1 -1
  56. package/skills/excalidraw/app/dist/assets/{ganttDiagram-EL5Y4UJY-DuJaTNOL.js → ganttDiagram-EL5Y4UJY-7cVW9NNi.js} +1 -1
  57. package/skills/excalidraw/app/dist/assets/{gitGraph-4MIJSDKK-DHGj5WCj.js → gitGraph-4MIJSDKK-BEY40yrV.js} +1 -1
  58. package/skills/excalidraw/app/dist/assets/{gitGraphDiagram-WWUBYQGX-DdvnVTzU.js → gitGraphDiagram-WWUBYQGX-B-Lfvb9V.js} +1 -1
  59. package/skills/excalidraw/app/dist/assets/{gl-ES-HMX3MZ6V-Cbr9hGRI.js → gl-ES-HMX3MZ6V-Df79IgqG.js} +1 -1
  60. package/skills/excalidraw/app/dist/assets/{he-IL-6SHJWFNN-B5Iboi8a.js → he-IL-6SHJWFNN-B2MduYIk.js} +1 -1
  61. package/skills/excalidraw/app/dist/assets/{hi-IN-IWLTKZ5I-k6swWOWn.js → hi-IN-IWLTKZ5I-DzfZntbn.js} +1 -1
  62. package/skills/excalidraw/app/dist/assets/{hu-HU-A5ZG7DT2-DaDlVvTE.js → hu-HU-A5ZG7DT2-ZCfdicPP.js} +1 -1
  63. package/skills/excalidraw/app/dist/assets/{id-ID-SAP4L64H-DiV3qfW7.js → id-ID-SAP4L64H-B3X6T4rm.js} +1 -1
  64. package/skills/excalidraw/app/dist/assets/image-GAAHSSAO-Bc_EwI1R.js +1 -0
  65. package/skills/excalidraw/app/dist/assets/{index-ChSm4O-k.js → index-B7WaxUYZ.js} +9 -9
  66. package/skills/excalidraw/app/dist/assets/{info-A6RAGUB7-EjytcsrH.js → info-A6RAGUB7-CL9tRM1h.js} +1 -1
  67. package/skills/excalidraw/app/dist/assets/{infoDiagram-27XIBGKW-D5p952K8.js → infoDiagram-27XIBGKW-R3aiYNu9.js} +1 -1
  68. package/skills/excalidraw/app/dist/assets/{ishikawaDiagram-5VMMS53U-Cl9GrB8p.js → ishikawaDiagram-5VMMS53U-DTESLM2m.js} +1 -1
  69. package/skills/excalidraw/app/dist/assets/{it-IT-JPQ66NNP-B3YDn8c7.js → it-IT-JPQ66NNP-BmfCqD8E.js} +1 -1
  70. package/skills/excalidraw/app/dist/assets/{ja-JP-DBVTYXUO-r8GJRlz8.js → ja-JP-DBVTYXUO-poCtVssg.js} +1 -1
  71. package/skills/excalidraw/app/dist/assets/{journeyDiagram-3NMN7TZE-DPL61tuu.js → journeyDiagram-3NMN7TZE-hXNITeol.js} +1 -1
  72. package/skills/excalidraw/app/dist/assets/{kaa-6HZHGXH3-CQXO0smT.js → kaa-6HZHGXH3-CMGm4sKC.js} +1 -1
  73. package/skills/excalidraw/app/dist/assets/{kab-KAB-ZGHBKWFO-CcnpslfP.js → kab-KAB-ZGHBKWFO-BZgJVp25.js} +1 -1
  74. package/skills/excalidraw/app/dist/assets/{kanban-definition-UXKFOSKX-CrEwB5Tt.js → kanban-definition-UXKFOSKX-C6s-BUcU.js} +1 -1
  75. package/skills/excalidraw/app/dist/assets/{kk-KZ-P5N5QNE5-DRBBwSLY.js → kk-KZ-P5N5QNE5-PW7XBW8H.js} +1 -1
  76. package/skills/excalidraw/app/dist/assets/{km-KH-HSX4SM5Z-aDqmSt43.js → km-KH-HSX4SM5Z-CiDy4UGb.js} +1 -1
  77. package/skills/excalidraw/app/dist/assets/{ko-KR-MTYHY66A-CrTedvVS.js → ko-KR-MTYHY66A-_I465Tst.js} +1 -1
  78. package/skills/excalidraw/app/dist/assets/{ku-TR-6OUDTVRD-hNhm9YQ0.js → ku-TR-6OUDTVRD-CxhJqtX5.js} +1 -1
  79. package/skills/excalidraw/app/dist/assets/{line-C4N-NEvZ.js → line-Dib4X-Fq.js} +1 -1
  80. package/skills/excalidraw/app/dist/assets/{lt-LT-XHIRWOB4-HeBwh7CL.js → lt-LT-XHIRWOB4-D3ULh7Sf.js} +1 -1
  81. package/skills/excalidraw/app/dist/assets/{lv-LV-5QDEKY6T-DXU4B6YW.js → lv-LV-5QDEKY6T-DM0hHv9d.js} +1 -1
  82. package/skills/excalidraw/app/dist/assets/{mermaid-parser.core-g7vsDY1V.js → mermaid-parser.core-BjygNlZP.js} +3 -3
  83. package/skills/excalidraw/app/dist/assets/{mindmap-definition-YA3MSWOX-D__pf1Yg.js → mindmap-definition-YA3MSWOX-Co0kxZ_S.js} +1 -1
  84. package/skills/excalidraw/app/dist/assets/{mr-IN-CRQNXWMA-BYsXnQkb.js → mr-IN-CRQNXWMA-Cm2Y9UIN.js} +1 -1
  85. package/skills/excalidraw/app/dist/assets/{my-MM-5M5IBNSE-C871ZDtQ.js → my-MM-5M5IBNSE-C0b98C9v.js} +1 -1
  86. package/skills/excalidraw/app/dist/assets/{nb-NO-T6EIAALU-Cm-d6hjp.js → nb-NO-T6EIAALU-DCbe9E9r.js} +1 -1
  87. package/skills/excalidraw/app/dist/assets/{nl-NL-IS3SIHDZ-XlSpYp1l.js → nl-NL-IS3SIHDZ-BGsegYOn.js} +1 -1
  88. package/skills/excalidraw/app/dist/assets/{nn-NO-6E72VCQL-BJPmp-_h.js → nn-NO-6E72VCQL-CKzpE5og.js} +1 -1
  89. package/skills/excalidraw/app/dist/assets/{oc-FR-POXYY2M6-D1rPi0z7.js → oc-FR-POXYY2M6-DU_G0Lbm.js} +1 -1
  90. package/skills/excalidraw/app/dist/assets/{pa-IN-N4M65BXN-hKsyWK74.js → pa-IN-N4M65BXN-Cg5tGrWt.js} +1 -1
  91. package/skills/excalidraw/app/dist/assets/{packet-AYTQ26CC-4PAjvWSX.js → packet-AYTQ26CC-5T6nzbJH.js} +1 -1
  92. package/skills/excalidraw/app/dist/assets/{pegDiagram-XKGWAZYB-CUfmvI5h.js → pegDiagram-XKGWAZYB-CYuI1-NC.js} +1 -1
  93. package/skills/excalidraw/app/dist/assets/percentages-BXMCSKIN-DR-sZ-Cj.js +1 -0
  94. package/skills/excalidraw/app/dist/assets/{pie-WAS4IAKB-B5FgQybZ.js → pie-WAS4IAKB-DI5e1lz7.js} +1 -1
  95. package/skills/excalidraw/app/dist/assets/{pieDiagram-E7YTZNPT-CU8w4Jjl.js → pieDiagram-E7YTZNPT-CidIif1z.js} +1 -1
  96. package/skills/excalidraw/app/dist/assets/{pl-PL-T2D74RX3-DOodWdkP.js → pl-PL-T2D74RX3-BZRHntUW.js} +1 -1
  97. package/skills/excalidraw/app/dist/assets/{pt-BR-5N22H2LF-dyjRDPfa.js → pt-BR-5N22H2LF-B9Tz5Aby.js} +1 -1
  98. package/skills/excalidraw/app/dist/assets/{pt-PT-UZXXM6DQ-DXb9iBoj.js → pt-PT-UZXXM6DQ-TFFmNb0q.js} +1 -1
  99. package/skills/excalidraw/app/dist/assets/{quadrantDiagram-AXDQQJYC-C60Jl0ho.js → quadrantDiagram-AXDQQJYC-BKQVbnpS.js} +1 -1
  100. package/skills/excalidraw/app/dist/assets/{radar-RG4KPBEZ-Bb6LV2iq.js → radar-RG4KPBEZ-DP3qJc6S.js} +1 -1
  101. package/skills/excalidraw/app/dist/assets/{railroad-74A4TZTK-DYFjVugV.js → railroad-74A4TZTK-9ukDzDF7.js} +1 -1
  102. package/skills/excalidraw/app/dist/assets/railroad-abnf-HS5TGJTU-S4oE0xhi.js +1 -0
  103. package/skills/excalidraw/app/dist/assets/railroad-ebnf-LZEXJU2U-ClCABo4z.js +1 -0
  104. package/skills/excalidraw/app/dist/assets/railroad-peg-WCYAUIDC-Cged2bPf.js +1 -0
  105. package/skills/excalidraw/app/dist/assets/{railroadDiagram-O6MQD6OU-D-Q-2PY3.js → railroadDiagram-O6MQD6OU-DnkrieKF.js} +1 -1
  106. package/skills/excalidraw/app/dist/assets/{requirementDiagram-BXWQKSXE-DUJGchGn.js → requirementDiagram-BXWQKSXE-B5jXr2XK.js} +1 -1
  107. package/skills/excalidraw/app/dist/assets/{ro-RO-JPDTUUEW-D2t-SdK7.js → ro-RO-JPDTUUEW-DXjMvI6V.js} +1 -1
  108. package/skills/excalidraw/app/dist/assets/{ru-RU-B4JR7IUQ-B3zBvqM8.js → ru-RU-B4JR7IUQ-CSxDpFDd.js} +1 -1
  109. package/skills/excalidraw/app/dist/assets/{sankeyDiagram-P5KCCOFB-B7gzsCzv.js → sankeyDiagram-P5KCCOFB-DA5BPuJI.js} +1 -1
  110. package/skills/excalidraw/app/dist/assets/{sequenceDiagram-WJ2MYXX4-C1gETguU.js → sequenceDiagram-WJ2MYXX4-BJKW-hN5.js} +1 -1
  111. package/skills/excalidraw/app/dist/assets/{si-LK-N5RQ5JYF-x9atOM2o.js → si-LK-N5RQ5JYF-B92qjwcf.js} +1 -1
  112. package/skills/excalidraw/app/dist/assets/{sk-SK-C5VTKIMK-Dc-U7HwG.js → sk-SK-C5VTKIMK-cBcXxEd5.js} +1 -1
  113. package/skills/excalidraw/app/dist/assets/{sl-SI-NN7IZMDC-DBErI-th.js → sl-SI-NN7IZMDC-DAxtv6Ub.js} +1 -1
  114. package/skills/excalidraw/app/dist/assets/{stateDiagram-D77RDMKH-ms90ttRw.js → stateDiagram-D77RDMKH-DccTl2o2.js} +1 -1
  115. package/skills/excalidraw/app/dist/assets/stateDiagram-v2-MP3YSRHH-BGa59Ao_.js +1 -0
  116. package/skills/excalidraw/app/dist/assets/subset-shared.chunk-DZ7VZc1A.js +1 -0
  117. package/skills/excalidraw/app/dist/assets/{subset-worker.chunk-BBqoITZT.js → subset-worker.chunk-DdulaO6i.js} +1 -1
  118. package/skills/excalidraw/app/dist/assets/{sv-SE-XGPEYMSR-DIhT7VhP.js → sv-SE-XGPEYMSR-CPvPJH47.js} +1 -1
  119. package/skills/excalidraw/app/dist/assets/{swimlanes-42K2YHIH-C03UW4hY.js → swimlanes-42K2YHIH-D7pjRFB9.js} +1 -1
  120. package/skills/excalidraw/app/dist/assets/swimlanesDiagram-VR7AAH4N-C3YPXYAD.js +8 -0
  121. package/skills/excalidraw/app/dist/assets/{ta-IN-2NMHFXQM-ollyGw17.js → ta-IN-2NMHFXQM-BBEC3f3N.js} +1 -1
  122. package/skills/excalidraw/app/dist/assets/{th-TH-HPSO5L25-GycTdvha.js → th-TH-HPSO5L25-DDDbbu5P.js} +1 -1
  123. package/skills/excalidraw/app/dist/assets/{timeline-definition-24CTP7MA-BWQ2CWiF.js → timeline-definition-24CTP7MA-B6HTpZKB.js} +1 -1
  124. package/skills/excalidraw/app/dist/assets/{tr-TR-DEFEU3FU-C0EQI1hS.js → tr-TR-DEFEU3FU-n3kHpRvH.js} +1 -1
  125. package/skills/excalidraw/app/dist/assets/{treeView-Q6P3EWNA-B4gjxheB.js → treeView-Q6P3EWNA-DB3IdO3v.js} +1 -1
  126. package/skills/excalidraw/app/dist/assets/{treemap-WGGIJYW6-C6ZNbWbw.js → treemap-WGGIJYW6-CdWayNO_.js} +1 -1
  127. package/skills/excalidraw/app/dist/assets/{uk-UA-QMV73CPH--gALSA3R.js → uk-UA-QMV73CPH-BIgtz66-.js} +1 -1
  128. package/skills/excalidraw/app/dist/assets/{vennDiagram-4TSXK5OY-DB2q0NWm.js → vennDiagram-4TSXK5OY-gyIQonJM.js} +1 -1
  129. package/skills/excalidraw/app/dist/assets/{vi-VN-M7AON7JQ-BU-don7N.js → vi-VN-M7AON7JQ-EHnHzhTl.js} +1 -1
  130. package/skills/excalidraw/app/dist/assets/{wardley-WFR3VGLG-CMlUPGFV.js → wardley-WFR3VGLG-D2SQfybF.js} +1 -1
  131. package/skills/excalidraw/app/dist/assets/{wardleyDiagram-VM6X3IG4-D7s4KunH.js → wardleyDiagram-VM6X3IG4-Cc0aycrS.js} +1 -1
  132. package/skills/excalidraw/app/dist/assets/{xychartDiagram-S5SC5T6Z-C3Ka4xi8.js → xychartDiagram-S5SC5T6Z-eXreaCYn.js} +1 -1
  133. package/skills/excalidraw/app/dist/assets/{zh-CN-LNUGB5OW-D-2E446q.js → zh-CN-LNUGB5OW-KvwjffQz.js} +1 -1
  134. package/skills/excalidraw/app/dist/assets/{zh-HK-E62DVLB3-cftaz5Rd.js → zh-HK-E62DVLB3-DoBuDyuy.js} +1 -1
  135. package/skills/excalidraw/app/dist/assets/{zh-TW-RAJ6MFWO-BO55Yqh9.js → zh-TW-RAJ6MFWO-BiI3UmNY.js} +1 -1
  136. package/skills/excalidraw/app/dist/index.html +1 -1
  137. package/skills/excalidraw/app/package.json +3 -0
  138. package/skills/excalidraw/app/src/main.jsx +73 -2
  139. package/skills/excalidraw/assets/examples/flowchart.excalidraw +1 -1
  140. package/skills/excalidraw/references/setup.md +36 -5
  141. package/skills/excalidraw/references/style.md +3 -3
  142. package/skills/excalidraw/references/sync-protocol.md +10 -2
  143. package/skills/excalidraw/scripts/excal.mjs +71 -37
  144. package/skills/excalidraw/scripts/excal.test.mjs +64 -0
  145. package/skills/excalidraw/app/dist/assets/architecture-7GRP2DOG-BECp2Zrk.js +0 -1
  146. package/skills/excalidraw/app/dist/assets/channel-Ca-ODdO0.js +0 -1
  147. package/skills/excalidraw/app/dist/assets/classDiagram-ZZMXUADV-DCPVGkLh.js +0 -1
  148. package/skills/excalidraw/app/dist/assets/classDiagram-v2-VYDZK3BY-DCPVGkLh.js +0 -1
  149. package/skills/excalidraw/app/dist/assets/eventmodeling-NTZA5JFV-160-dgiE.js +0 -1
  150. package/skills/excalidraw/app/dist/assets/flowDiagram-HODETNUW-XE0UaaJt.js +0 -1
  151. package/skills/excalidraw/app/dist/assets/image-GAAHSSAO-stUGVFAH.js +0 -1
  152. package/skills/excalidraw/app/dist/assets/percentages-BXMCSKIN-CfhMReVO.js +0 -1
  153. package/skills/excalidraw/app/dist/assets/railroad-abnf-HS5TGJTU-CCpAO9jj.js +0 -1
  154. package/skills/excalidraw/app/dist/assets/railroad-ebnf-LZEXJU2U-BTtgE2v0.js +0 -1
  155. package/skills/excalidraw/app/dist/assets/railroad-peg-WCYAUIDC-qT7-4WNw.js +0 -1
  156. package/skills/excalidraw/app/dist/assets/stateDiagram-v2-MP3YSRHH-BLT-8Bst.js +0 -1
  157. package/skills/excalidraw/app/dist/assets/subset-shared.chunk-JkuiVUYR.js +0 -1
  158. package/skills/excalidraw/app/dist/assets/swimlanesDiagram-VR7AAH4N-OUQB4gXu.js +0 -8
@@ -1,4 +1,4 @@
1
- import"./index-ChSm4O-k.js";var e={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:`绘制 Canvas`,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:`Excalidraw 素材库`,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:`Prompt`},t={noItems:`尚未添加任何项目……`,hint_emptyLibrary:`选中画布上的项目添加到此处,或从下方的公共素材库中导入。`,hint_emptyPrivateLibrary:`选中画布上的项目添加到此处。`},n={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:`点击以开始交互`},r={clearReset:`这将会清除整个画布。您是否要继续?`,couldNotCreateShareableLink:`无法创建共享链接`,couldNotCreateShareableLinkTooBig:`无法创建可共享链接:画布过大`,couldNotLoadInvalidFile:`无法加载无效的文件`,importBackendFailed:`从后端导入失败。`,cannotExportEmptyCanvas:`无法导出空白画布。`,couldNotCopyToClipboard:`无法复制到剪贴板。`,decryptFailed:`无法解密数据。`,uploadedSecurly:`上传已被端到端加密保护,这意味着 Excalidraw 的服务器和第三方都无法读取内容。`,loadSceneOverridePrompt:`加载外部绘图将取代您现有的内容。您想要继续吗?`,collabStopOverridePrompt:`停止会话将覆盖您先前本地存储的绘图。 您确定吗?
1
+ import"./index-B7WaxUYZ.js";var e={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:`绘制 Canvas`,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:`Excalidraw 素材库`,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:`Prompt`},t={noItems:`尚未添加任何项目……`,hint_emptyLibrary:`选中画布上的项目添加到此处,或从下方的公共素材库中导入。`,hint_emptyPrivateLibrary:`选中画布上的项目添加到此处。`},n={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:`点击以开始交互`},r={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
  您的改动将不会被保存!`},i={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:`如果禁用此设置无法修复文本元素的显示,请在 GitHub 上提交一个 <issueLink>issue</issueLink> ,或者在 <discordLink>Discord</discordLink> 上反馈`},libraryElementTypeError:{embeddable:`嵌入的元素不能被添加到素材库。`,iframe:`不能将 IFrame 元素添加到素材库中。`,image:`我们不久将支持添加图片到素材库`},asyncPasteFailedOnRead:`无法粘贴(无法读取系统剪贴板)。`,asyncPasteFailedOnParse:`无法粘贴。`,copyToSystemClipboardFailed:`无法复制到剪贴板。`},a={selection:`选择`,image:`插入图像`,rectangle:`矩形`,diamond:`菱形`,ellipse:`椭圆`,arrow:`箭头`,line:`线条`,freedraw:`自由书写`,text:`文字`,library:`素材库`,lock:`绘制后保持所选的工具栏状态`,penMode:`笔模式 – 避免误触`,link:`为选中的形状添加/更新链接`,eraser:`橡皮`,frame:`画框工具`,magicframe:`线框图至代码`,embeddable:`嵌入网页`,laser:`激光笔`,hand:`抓手(平移工具)`,extraTools:`更多工具`,mermaidToExcalidraw:`Mermaid 至 Excalidraw`,magicSettings:`AI 设置`},o={canvasActions:`画布动作`,selectedShapeActions:`选定形状操作`,shapes:`形状`},s={canvasPanning:`要移动画布,请按住鼠标滚轮或空格键同时拖拽鼠标,或使用抓手工具。`,linearElement:`点击创建多个点 拖动创建直线`,freeDraw:`点击并拖动,完成时松开`,text:`提示:您也可以使用选择工具双击任意位置来添加文字`,embeddable:`点击并拖动以创建嵌入网页`,text_selected:`双击或按回车键以编辑文本`,text_editing:`按下 Escape 或 CtrlOrCmd+ENTER 完成编辑`,linearElementMulti:`点击最后一个点或按下 Esc/Enter 来完成`,lockAngle:`可以按住 Shift 来约束角度`,resize:`您可以按住SHIFT来限制比例大小,
@@ -1 +1 @@
1
- import"./index-ChSm4O-k.js";var e={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:``},t={noItems:``,hint_emptyLibrary:``,hint_emptyPrivateLibrary:``},n={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:``},r={clearReset:`【‼️ 警告 ‼️ 無法復原的動作】你確定要清空呢塊畫布嗎?`,couldNotCreateShareableLink:``,couldNotCreateShareableLinkTooBig:``,couldNotLoadInvalidFile:``,importBackendFailed:``,cannotExportEmptyCanvas:`無嘢可以匯出喎~畫吓嘢先?`,couldNotCopyToClipboard:``,decryptFailed:``,uploadedSecurly:``,loadSceneOverridePrompt:``,collabStopOverridePrompt:``,errorAddingToLibrary:``,errorRemovingFromLibrary:``,confirmAddLibrary:``,imageDoesNotContainScene:``,cannotRestoreFromImage:``,invalidSceneUrl:``,resetLibrary:``,removeItemsFromsLibrary:``,invalidEncryptionKey:``,collabOfflineWarning:``},i={unsupportedFileType:``,imageInsertError:``,fileTooBig:``,svgImageInsertError:``,failedToFetchImage:``,invalidSVGString:``,cannotResolveCollabServer:``,importLibraryError:``,collabSaveFailed:``,collabSaveFailed_sizeExceeded:``,imageToolNotSupported:``,brave_measure_text_error:{line1:``,line2:``,line3:``,line4:``},libraryElementTypeError:{embeddable:``,iframe:``,image:``},asyncPasteFailedOnRead:``,asyncPasteFailedOnParse:``,copyToSystemClipboardFailed:``},a={selection:``,image:``,rectangle:``,diamond:``,ellipse:``,arrow:``,line:``,freedraw:``,text:``,library:``,lock:``,penMode:``,link:``,eraser:``,frame:``,magicframe:``,embeddable:``,laser:``,hand:``,extraTools:``,mermaidToExcalidraw:``,magicSettings:``},o={canvasActions:`畫布動作`,selectedShapeActions:``,shapes:``},s={canvasPanning:``,linearElement:``,freeDraw:``,text:``,embeddable:``,text_selected:``,text_editing:``,linearElementMulti:``,lockAngle:``,resize:``,resizeImage:``,rotate:``,lineEditor_info:``,lineEditor_pointSelected:``,lineEditor_nothingSelected:``,placeImage:``,publishLibrary:``,bindTextToElement:``,deepBoxSelect:``,eraserRevert:``,firefox_clipboard_write:``,disableSnapping:``},c={cannotShowPreview:`無法顯示預覽`,canvasTooBig:`塊畫布太大啦,縮細啲先啦?`,canvasTooBigTip:``},l={headingMain:``,clearCanvasMessage:`如果重新整理頁面都係睇唔到,你可以<button>清空畫布</button>`,clearCanvasCaveat:`(注意:呢個動作會直接丟棄你嘅作品,並且無法復原)`,trackedToSentry:``,openIssueMessage:``,sceneContent:``},u={desc_intro:``,desc_privacy:``,button_startSession:``,button_stopSession:``,desc_inProgressIntro:``,desc_shareLink:``,desc_exitSession:``,shareTitle:``},d={title:``},f={disk_title:``,disk_details:``,disk_button:``,link_title:``,link_details:``,link_button:``,excalidrawplus_description:``,excalidrawplus_button:``,excalidrawplus_exportError:``},p={blog:``,click:``,deepSelect:``,deepBoxSelect:``,curvedArrow:``,curvedLine:``,documentation:``,doubleClick:``,drag:``,editor:``,editLineArrowPoints:``,editText:``,github:``,howto:``,or:``,preventBinding:``,tools:``,shortcuts:``,textFinish:``,textNewLine:``,title:``,view:``,zoomToFit:``,zoomToSelection:``,toggleElementLock:``,movePageUpDown:``,movePageLeftRight:``},m={title:``},h={title:``,itemName:``,authorName:``,githubUsername:``,twitterUsername:``,libraryName:``,libraryDesc:``,website:``,placeholder:{authorName:``,libraryName:``,libraryDesc:``,githubHandle:``,twitterHandle:``,website:``},errors:{required:``,website:``},noteDescription:``,noteGuidelines:``,noteLicense:``,noteItems:``,atleastOneLibItem:``,republishWarning:``},g={title:``,content:``},_={resetLibrary:``,removeItemsFromLib:``},v={header:``,label:{withBackground:``,onlySelected:``,darkMode:``,embedScene:``,scale:``,padding:``},tooltip:{embedScene:``},title:{exportToPng:``,exportToSvg:``,copyPngToClipboard:``},button:{exportToPng:``,exportToSvg:``,copyPngToClipboard:``}},y={tooltip:``,link:``},b={angle:``,element:``,elements:``,height:``,scene:``,selected:``,storage:``,title:``,total:``,version:``,versionCopy:``,versionNotAvailable:``,width:``},x={addedToLibrary:``,copyStyles:``,copyToClipboard:``,copyToClipboardAsPng:``,fileSaved:``,fileSavedToFilename:``,canvas:`畫布`,selection:``,pasteAsSingleElement:``,unableToEmbed:``,unrecognizedLinkFormat:``},S={transparent:``,black:``,white:``,red:``,pink:``,grape:``,violet:``,gray:``,blue:``,cyan:``,teal:``,green:``,yellow:``,orange:``,bronze:``},C={app:{center_heading:``,center_heading_plus:``,menuHint:``},defaults:{menuHint:``,center_heading:``,toolbarHint:``,helpHint:``}},w={mostUsedCustomColors:``,colors:``,shades:``,hexCode:``,noShades:``},T={action:{exportToImage:{title:``,button:``,description:``},saveToDisk:{title:``,button:``,description:``},excalidrawPlus:{title:``,button:``,description:``}},modal:{loadFromFile:{title:``,button:``,description:``},shareableLink:{title:``,button:``,description:``}}},E={title:``,button:``,description:``,syntax:``,preview:``},D={labels:e,library:t,buttons:n,alerts:r,errors:i,toolBar:a,headings:o,hints:s,canvasError:c,errorSplash:l,roomDialog:u,errorDialog:d,exportDialog:f,helpDialog:p,clearCanvasDialog:m,publishDialog:h,publishSuccessDialog:g,confirmDialog:_,imageExportDialog:v,encrypted:y,stats:b,toast:x,colors:S,welcomeScreen:C,colorPicker:w,overwriteConfirm:T,mermaid:E};export{r as alerts,n as buttons,c as canvasError,m as clearCanvasDialog,w as colorPicker,S as colors,_ as confirmDialog,D as default,y as encrypted,d as errorDialog,l as errorSplash,i as errors,f as exportDialog,o as headings,p as helpDialog,s as hints,v as imageExportDialog,e as labels,t as library,E as mermaid,T as overwriteConfirm,h as publishDialog,g as publishSuccessDialog,u as roomDialog,b as stats,x as toast,a as toolBar,C as welcomeScreen};
1
+ import"./index-B7WaxUYZ.js";var e={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:``},t={noItems:``,hint_emptyLibrary:``,hint_emptyPrivateLibrary:``},n={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:``},r={clearReset:`【‼️ 警告 ‼️ 無法復原的動作】你確定要清空呢塊畫布嗎?`,couldNotCreateShareableLink:``,couldNotCreateShareableLinkTooBig:``,couldNotLoadInvalidFile:``,importBackendFailed:``,cannotExportEmptyCanvas:`無嘢可以匯出喎~畫吓嘢先?`,couldNotCopyToClipboard:``,decryptFailed:``,uploadedSecurly:``,loadSceneOverridePrompt:``,collabStopOverridePrompt:``,errorAddingToLibrary:``,errorRemovingFromLibrary:``,confirmAddLibrary:``,imageDoesNotContainScene:``,cannotRestoreFromImage:``,invalidSceneUrl:``,resetLibrary:``,removeItemsFromsLibrary:``,invalidEncryptionKey:``,collabOfflineWarning:``},i={unsupportedFileType:``,imageInsertError:``,fileTooBig:``,svgImageInsertError:``,failedToFetchImage:``,invalidSVGString:``,cannotResolveCollabServer:``,importLibraryError:``,collabSaveFailed:``,collabSaveFailed_sizeExceeded:``,imageToolNotSupported:``,brave_measure_text_error:{line1:``,line2:``,line3:``,line4:``},libraryElementTypeError:{embeddable:``,iframe:``,image:``},asyncPasteFailedOnRead:``,asyncPasteFailedOnParse:``,copyToSystemClipboardFailed:``},a={selection:``,image:``,rectangle:``,diamond:``,ellipse:``,arrow:``,line:``,freedraw:``,text:``,library:``,lock:``,penMode:``,link:``,eraser:``,frame:``,magicframe:``,embeddable:``,laser:``,hand:``,extraTools:``,mermaidToExcalidraw:``,magicSettings:``},o={canvasActions:`畫布動作`,selectedShapeActions:``,shapes:``},s={canvasPanning:``,linearElement:``,freeDraw:``,text:``,embeddable:``,text_selected:``,text_editing:``,linearElementMulti:``,lockAngle:``,resize:``,resizeImage:``,rotate:``,lineEditor_info:``,lineEditor_pointSelected:``,lineEditor_nothingSelected:``,placeImage:``,publishLibrary:``,bindTextToElement:``,deepBoxSelect:``,eraserRevert:``,firefox_clipboard_write:``,disableSnapping:``},c={cannotShowPreview:`無法顯示預覽`,canvasTooBig:`塊畫布太大啦,縮細啲先啦?`,canvasTooBigTip:``},l={headingMain:``,clearCanvasMessage:`如果重新整理頁面都係睇唔到,你可以<button>清空畫布</button>`,clearCanvasCaveat:`(注意:呢個動作會直接丟棄你嘅作品,並且無法復原)`,trackedToSentry:``,openIssueMessage:``,sceneContent:``},u={desc_intro:``,desc_privacy:``,button_startSession:``,button_stopSession:``,desc_inProgressIntro:``,desc_shareLink:``,desc_exitSession:``,shareTitle:``},d={title:``},f={disk_title:``,disk_details:``,disk_button:``,link_title:``,link_details:``,link_button:``,excalidrawplus_description:``,excalidrawplus_button:``,excalidrawplus_exportError:``},p={blog:``,click:``,deepSelect:``,deepBoxSelect:``,curvedArrow:``,curvedLine:``,documentation:``,doubleClick:``,drag:``,editor:``,editLineArrowPoints:``,editText:``,github:``,howto:``,or:``,preventBinding:``,tools:``,shortcuts:``,textFinish:``,textNewLine:``,title:``,view:``,zoomToFit:``,zoomToSelection:``,toggleElementLock:``,movePageUpDown:``,movePageLeftRight:``},m={title:``},h={title:``,itemName:``,authorName:``,githubUsername:``,twitterUsername:``,libraryName:``,libraryDesc:``,website:``,placeholder:{authorName:``,libraryName:``,libraryDesc:``,githubHandle:``,twitterHandle:``,website:``},errors:{required:``,website:``},noteDescription:``,noteGuidelines:``,noteLicense:``,noteItems:``,atleastOneLibItem:``,republishWarning:``},g={title:``,content:``},_={resetLibrary:``,removeItemsFromLib:``},v={header:``,label:{withBackground:``,onlySelected:``,darkMode:``,embedScene:``,scale:``,padding:``},tooltip:{embedScene:``},title:{exportToPng:``,exportToSvg:``,copyPngToClipboard:``},button:{exportToPng:``,exportToSvg:``,copyPngToClipboard:``}},y={tooltip:``,link:``},b={angle:``,element:``,elements:``,height:``,scene:``,selected:``,storage:``,title:``,total:``,version:``,versionCopy:``,versionNotAvailable:``,width:``},x={addedToLibrary:``,copyStyles:``,copyToClipboard:``,copyToClipboardAsPng:``,fileSaved:``,fileSavedToFilename:``,canvas:`畫布`,selection:``,pasteAsSingleElement:``,unableToEmbed:``,unrecognizedLinkFormat:``},S={transparent:``,black:``,white:``,red:``,pink:``,grape:``,violet:``,gray:``,blue:``,cyan:``,teal:``,green:``,yellow:``,orange:``,bronze:``},C={app:{center_heading:``,center_heading_plus:``,menuHint:``},defaults:{menuHint:``,center_heading:``,toolbarHint:``,helpHint:``}},w={mostUsedCustomColors:``,colors:``,shades:``,hexCode:``,noShades:``},T={action:{exportToImage:{title:``,button:``,description:``},saveToDisk:{title:``,button:``,description:``},excalidrawPlus:{title:``,button:``,description:``}},modal:{loadFromFile:{title:``,button:``,description:``},shareableLink:{title:``,button:``,description:``}}},E={title:``,button:``,description:``,syntax:``,preview:``},D={labels:e,library:t,buttons:n,alerts:r,errors:i,toolBar:a,headings:o,hints:s,canvasError:c,errorSplash:l,roomDialog:u,errorDialog:d,exportDialog:f,helpDialog:p,clearCanvasDialog:m,publishDialog:h,publishSuccessDialog:g,confirmDialog:_,imageExportDialog:v,encrypted:y,stats:b,toast:x,colors:S,welcomeScreen:C,colorPicker:w,overwriteConfirm:T,mermaid:E};export{r as alerts,n as buttons,c as canvasError,m as clearCanvasDialog,w as colorPicker,S as colors,_ as confirmDialog,D as default,y as encrypted,d as errorDialog,l as errorSplash,i as errors,f as exportDialog,o as headings,p as helpDialog,s as hints,v as imageExportDialog,e as labels,t as library,E as mermaid,T as overwriteConfirm,h as publishDialog,g as publishSuccessDialog,u as roomDialog,b as stats,x as toast,a as toolBar,C as welcomeScreen};
@@ -1,4 +1,4 @@
1
- import"./index-ChSm4O-k.js";var e={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:`加上 "Made with Excalidraw" 浮水印`,handDrawn:`手寫`,normal:`一般`,code:`代碼`,small:`小`,medium:`中`,large:`大`,veryLarge:`特大`,solid:`實心`,hachure:`斜線筆觸`,zigzag:`Z字形`,crossHatch:`交叉筆觸`,thin:`細`,bold:`粗`,left:`左側`,center:`置中`,right:`右側`,extraBold:`極粗`,architect:`精確`,artist:`藝術`,cartoonist:`卡通`,fileTitle:`檔案名稱`,colorPicker:`色彩選擇工具`,canvasColors:`使用於畫布`,canvasBackground:`Canvas 背景`,drawingCanvas:`繪圖 canvas`,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:`Excalidraw 資料庫`,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:`提示詞`},t={noItems:`尚未加入任何物件...`,hint_emptyLibrary:`選取畫布上的物件以加入,或從下方的公開 repository 中安裝資料庫`,hint_emptyPrivateLibrary:`選擇畫布上的物件以在此加入`},n={clearReset:`重置 canvas`,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:`點擊以互動`},r={clearReset:`這將會清除整個 canvas。你確定嗎?`,couldNotCreateShareableLink:`無法建立共享連結。`,couldNotCreateShareableLinkTooBig:`無法建立共享連結:場景太大`,couldNotLoadInvalidFile:`無法讀取失效的檔案。`,importBackendFailed:`後端讀取失敗。`,cannotExportEmptyCanvas:`無法輸出空白的 canvas。`,couldNotCopyToClipboard:`無法複製到剪貼簿`,decryptFailed:`無法解密資料。`,uploadedSecurly:`上傳已通過 end-to-end 加密,Excalidraw 伺服器和第三方無法皆讀取其內容。`,loadSceneOverridePrompt:`讀取外部圖樣將取代目前的內容。是否要繼續?`,collabStopOverridePrompt:`停止連線將覆蓋您先前於本機儲存的繪圖進度,是否確認?
1
+ import"./index-B7WaxUYZ.js";var e={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:`加上 "Made with Excalidraw" 浮水印`,handDrawn:`手寫`,normal:`一般`,code:`代碼`,small:`小`,medium:`中`,large:`大`,veryLarge:`特大`,solid:`實心`,hachure:`斜線筆觸`,zigzag:`Z字形`,crossHatch:`交叉筆觸`,thin:`細`,bold:`粗`,left:`左側`,center:`置中`,right:`右側`,extraBold:`極粗`,architect:`精確`,artist:`藝術`,cartoonist:`卡通`,fileTitle:`檔案名稱`,colorPicker:`色彩選擇工具`,canvasColors:`使用於畫布`,canvasBackground:`Canvas 背景`,drawingCanvas:`繪圖 canvas`,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:`Excalidraw 資料庫`,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:`提示詞`},t={noItems:`尚未加入任何物件...`,hint_emptyLibrary:`選取畫布上的物件以加入,或從下方的公開 repository 中安裝資料庫`,hint_emptyPrivateLibrary:`選擇畫布上的物件以在此加入`},n={clearReset:`重置 canvas`,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:`點擊以互動`},r={clearReset:`這將會清除整個 canvas。你確定嗎?`,couldNotCreateShareableLink:`無法建立共享連結。`,couldNotCreateShareableLinkTooBig:`無法建立共享連結:場景太大`,couldNotLoadInvalidFile:`無法讀取失效的檔案。`,importBackendFailed:`後端讀取失敗。`,cannotExportEmptyCanvas:`無法輸出空白的 canvas。`,couldNotCopyToClipboard:`無法複製到剪貼簿`,decryptFailed:`無法解密資料。`,uploadedSecurly:`上傳已通過 end-to-end 加密,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
  變更無法儲存!`},i={unsupportedFileType:`不支援的檔案類型。`,imageInsertError:`無法插入圖片。請稍後再試…`,fileTooBig:`檔案過大。可接受的最大尺寸為 {{maxSize}} 。`,svgImageInsertError:`無法插入 SVG 圖片。此 SVG 檔案有問題。`,failedToFetchImage:`無法獲取圖片。`,invalidSVGString:`無效的 SVG。`,cannotResolveCollabServer:`無法連結至 collab 伺服器。請重新整理後再試一次。`,importLibraryError:`無法載入資料庫`,collabSaveFailed:`無法儲存至後端資料庫。若此問題持續發生,請將檔案儲存於本機以確保資料不會遺失。`,collabSaveFailed_sizeExceeded:`無法儲存至後端資料庫,可能的原因為畫布尺寸過大。請將檔案儲存於本機以確保資料不會遺失。`,imageToolNotSupported:`圖片已停用`,brave_measure_text_error:{line1:`看起來您開啟了 Brave 瀏覽器的 <bold>Aggressively Block Fingerprinting</bold> 設定。`,line2:`這可能造成您畫布中 <bold>文字元素</bold> 的異常。`,line3:`我們強烈建議您關閉此設定。您可以依照 <link>這些步驟</link> 來進行。`,line4:`若關閉此設定並未修復文字元素的顯示問題,請回報於我們 GitHub 上的 <issueLink>issue</issueLink>,或在 <discordLink>Discord</discordLink> 上告訴我們。`},libraryElementTypeError:{embeddable:`可嵌入元素無法加入資料庫`,iframe:`IFrame 元素無法加入資料庫`,image:`即將支援加入圖片至資料庫!`},asyncPasteFailedOnRead:`無法貼上(無法由系統剪貼簿讀入)`,asyncPasteFailedOnParse:`無法貼上`,copyToSystemClipboardFailed:`無法複製至剪貼簿`},a={selection:`選取`,image:`插入圖片`,rectangle:`長方形`,diamond:`菱形`,ellipse:`橢圓`,arrow:`箭頭`,line:`線條`,freedraw:`繪圖`,text:`文字`,library:`資料庫`,lock:`可連續使用選取的工具`,penMode:`筆模式 - 避免觸摸`,link:`為所選的形狀增加\b/更新連結`,eraser:`橡皮擦`,frame:`框架工具`,magicframe:`線框稿轉為程式碼`,embeddable:`嵌入網站`,laser:`雷射筆`,hand:`手形(平移工具)`,extraTools:`更多工具`,mermaidToExcalidraw:`Mermaid 至 Excalidraw`,magicSettings:`AI 設定`},o={canvasActions:`canvas 動作`,selectedShapeActions:`選取圖形動作`,shapes:`形狀`},s={canvasPanning:`若要移動畫布,請在拖曳時按住滑鼠滾輪或空白鍵,或使用手形工具`,linearElement:`點擊以繪製多點曲線;或拖曳以繪製直線`,freeDraw:`點擊並拖曳來繪圖,放開即結束`,text:`提示:亦可使用選取工具在任何地方雙擊來加入文字`,embeddable:`點擊並拖移以建立嵌入網站`,text_selected:`雙擊滑鼠或按 Enter 以編輯文字`,text_editing:`按跳脫鍵或 Ctrl 或 Cmd + Enter 以結束編輯`,linearElementMulti:`按下 Escape 或 Enter 以結束繪製`,lockAngle:`按住 SHIFT 可限制旋轉角度`,resize:`縮放時按住 Shift 可保持原比例縮放;\\n按住 Alt 可由中心點進行縮放`,resizeImage:`按住 SHIFT 可任意縮放,按住 ALT 可由中央縮放。`,rotate:`旋轉時按住 Shift 可限制旋轉角度`,lineEditor_info:`按住 Ctrl 或 Cmd 並雙擊或按住 Ctrl 或 Cmd + Enter 來編輯控制點`,lineEditor_pointSelected:`按下 Delete 可移除錨點;Ctrl 或 Cmd + D 可複製;或可拖曳來移動`,lineEditor_nothingSelected:`選擇要編輯的錨點(按住 SHIFT 可多選),
@@ -6,7 +6,7 @@
6
6
  <title>Excalidraw</title>
7
7
  <script>window.EXCALIDRAW_ASSET_PATH = "/";</script>
8
8
  <style>html,body,#root{height:100%;margin:0}</style>
9
- <script type="module" crossorigin src="/assets/index-ChSm4O-k.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-B7WaxUYZ.js"></script>
10
10
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-Dd_uD5pT.js">
11
11
  <link rel="stylesheet" crossorigin href="/assets/index-DtRVLazd.css">
12
12
  </head>
@@ -3,6 +3,9 @@
3
3
  "private": true,
4
4
  "version": "0.1.0",
5
5
  "type": "module",
6
+ "engines": {
7
+ "node": "^20.19.0 || >=22.12.0"
8
+ },
6
9
  "scripts": {
7
10
  "build": "vite build",
8
11
  "dev": "vite"
@@ -3,6 +3,7 @@ import {
3
3
  convertToExcalidrawElements,
4
4
  Excalidraw,
5
5
  exportToBlob,
6
+ FONT_FAMILY,
6
7
  restoreElements,
7
8
  serializeAsJSON,
8
9
  } from "@excalidraw/excalidraw";
@@ -27,6 +28,33 @@ const CLIENT_ID = crypto.randomUUID();
27
28
  const SAVE_DEBOUNCE_MS = 400;
28
29
  const RESTORE_OPTS = { refreshDimensions: true, repairBindings: true };
29
30
  const randomNonce = () => Math.floor(Math.random() * 2 ** 31);
31
+ const FONT_NAME = Object.fromEntries(Object.entries(FONT_FAMILY).map(([name, id]) => [id, name]));
32
+
33
+ /** Same font stack Excalidraw measures and renders text with. */
34
+ function fontString(fontSize = 20, fontFamily = FONT_FAMILY.Excalifont) {
35
+ const name = FONT_NAME[fontFamily] ?? "Excalifont";
36
+ const cjk = fontFamily === FONT_FAMILY.Excalifont ? ", Xiaolai" : "";
37
+ return `${fontSize}px ${name}${cjk}, Segoe UI Emoji`;
38
+ }
39
+
40
+ /**
41
+ * Load the glyphs the scene's text needs before measuring it. Excalidraw
42
+ * measures with whatever font is ready, and CJK glyphs (Xiaolai) load lazily per
43
+ * unicode-range chunk; a width measured with a fallback font stays in the
44
+ * element after the real font arrives, and the renderer clips text to it.
45
+ */
46
+ async function loadTextFonts(elements) {
47
+ const chars = new Map();
48
+ for (const e of elements) {
49
+ if (e.isDeleted) continue;
50
+ const src = e.type === "text" ? e : e.label;
51
+ if (typeof src?.text !== "string" || !src.text) continue;
52
+ const font = fontString(src.fontSize, src.fontFamily);
53
+ chars.set(font, (chars.get(font) ?? "") + src.text);
54
+ }
55
+ await Promise.all([...chars].map(([font, text]) => document.fonts.load(font, text).catch(() => [])));
56
+ await document.fonts.ready;
57
+ }
30
58
 
31
59
  async function api(pathname, init = {}) {
32
60
  return fetch(`/api/files/${FILE_ID}${pathname}`, {
@@ -132,6 +160,7 @@ function App() {
132
160
  const API = excalidrawAPI;
133
161
  const local = API.getSceneElementsIncludingDeleted();
134
162
  const prepared = await prepareScene(content, local);
163
+ await loadTextFonts(prepared.elements);
135
164
  // Label centering needs the text size re-measured by restoreElements.
136
165
  const { elements: remote } = recenterLabels(restoreElements(prepared.elements, local, RESTORE_OPTS));
137
166
  // Anything restore/recenter fixed (dangling bindings, text size, label
@@ -199,6 +228,33 @@ function App() {
199
228
  [excalidrawAPI, save],
200
229
  );
201
230
 
231
+ /**
232
+ * Re-measure text with the fonts that are loaded now and save if any size
233
+ * changed. Covers glyphs that finished loading after the last measurement
234
+ * (Excalidraw only repaints on font load; it keeps the stale width).
235
+ */
236
+ const refitText = useCallback(async () => {
237
+ const API = excalidrawAPI;
238
+ if (!s.ready || API.getAppState().editingTextElement) return false;
239
+ const current = API.getSceneElementsIncludingDeleted();
240
+ await loadTextFonts(current);
241
+ const live = current.filter((e) => !e.isDeleted);
242
+ const restored = new Map(restoreElements(live, null, RESTORE_OPTS).map((e) => [e.id, e]));
243
+ const merged = current.map((e) => (e.type === "text" && restored.has(e.id) ? restored.get(e.id) : e));
244
+ const { elements: fitted } = recenterLabels(merged);
245
+ let changed = 0;
246
+ const next = fitted.map((e, i) => {
247
+ const prev = current[i];
248
+ if (e.type !== "text" || contentKey(prev) === contentKey(e)) return prev;
249
+ changed++;
250
+ return { ...e, version: prev.version + 1, versionNonce: randomNonce() };
251
+ });
252
+ if (!changed) return false;
253
+ API.updateScene({ elements: next, captureUpdate: CaptureUpdateAction.NEVER });
254
+ await save();
255
+ return true;
256
+ }, [excalidrawAPI, save]);
257
+
202
258
  const snapshot = useCallback(
203
259
  async ({ reqId, rev }) => {
204
260
  const API = excalidrawAPI;
@@ -219,7 +275,8 @@ function App() {
219
275
  );
220
276
  return;
221
277
  }
222
- await document.fonts.ready;
278
+ // Fix stale text sizes first so the PNG matches what the window shows.
279
+ await enqueue(refitText);
223
280
  try {
224
281
  const blob = await exportToBlob({
225
282
  elements: API.getSceneElements(),
@@ -237,9 +294,23 @@ function App() {
237
294
  await post(JSON.stringify({ error: err.message }), "application/json");
238
295
  }
239
296
  },
240
- [excalidrawAPI],
297
+ [excalidrawAPI, refitText],
241
298
  );
242
299
 
300
+ useEffect(() => {
301
+ if (!excalidrawAPI) return;
302
+ let timer;
303
+ const onFontsLoaded = () => {
304
+ clearTimeout(timer);
305
+ timer = setTimeout(() => enqueue(refitText), 150);
306
+ };
307
+ document.fonts.addEventListener("loadingdone", onFontsLoaded);
308
+ return () => {
309
+ clearTimeout(timer);
310
+ document.fonts.removeEventListener("loadingdone", onFontsLoaded);
311
+ };
312
+ }, [excalidrawAPI, refitText]);
313
+
243
314
  useEffect(() => {
244
315
  if (!excalidrawAPI) return;
245
316
  window.__excal = { api: excalidrawAPI, state: s };
@@ -6,7 +6,7 @@
6
6
  { "type": "text", "id": "title", "x": 200, "y": 20, "text": "주문 처리 흐름", "fontSize": 28 },
7
7
  { "type": "ellipse", "id": "start", "x": 240, "y": 90, "width": 160, "height": 64, "backgroundColor": "#b2f2bb", "fillStyle": "solid", "label": { "text": "시작" } },
8
8
  { "type": "rectangle", "id": "order", "x": 220, "y": 214, "width": 200, "height": 72, "backgroundColor": "#a5d8ff", "fillStyle": "solid", "roundness": { "type": 3 }, "label": { "text": "주문 접수" } },
9
- { "type": "diamond", "id": "stock", "x": 210, "y": 346, "width": 220, "height": 120, "backgroundColor": "#ffec99", "fillStyle": "solid", "label": { "text": "재고 있음?" } },
9
+ { "type": "diamond", "id": "stock", "x": 200, "y": 346, "width": 240, "height": 120, "backgroundColor": "#ffec99", "fillStyle": "solid", "label": { "text": "재고 있음?" } },
10
10
  { "type": "rectangle", "id": "ship", "x": 220, "y": 526, "width": 200, "height": 72, "backgroundColor": "#a5d8ff", "fillStyle": "solid", "roundness": { "type": 3 }, "label": { "text": "배송 준비" } },
11
11
  { "type": "rectangle", "id": "backorder", "x": 520, "y": 370, "width": 200, "height": 72, "backgroundColor": "#ffc9c9", "fillStyle": "solid", "roundness": { "type": 3 }, "label": { "text": "입고 대기 알림" } },
12
12
  { "type": "arrow", "id": "a1", "start": { "id": "start" }, "end": { "id": "order" } },
@@ -2,17 +2,15 @@
2
2
 
3
3
  빌드된 편집기 앱이 패키지에 들어 있어 따로 설치·빌드할 것은 없다. 창을 띄울 브라우저만 필요하다.
4
4
 
5
- ## 확인
5
+ ## 1. 브라우저 확인
6
6
 
7
7
  ```bash
8
8
  open -Ra "Google Chrome" && echo ok # macOS
9
9
  ```
10
10
 
11
- `ok`가 나오면 설정할 것이 없다. Linux는 `google-chrome`, `google-chrome-stable`, `chromium`, `chromium-browser` 중 하나가 PATH에 있으면 된다.
11
+ `ok`가 나오면 다음 단계로 간다. Linux는 `google-chrome`, `google-chrome-stable`, `chromium`, `chromium-browser` 중 하나가 PATH에 있으면 된다.
12
12
 
13
- ## 설치 (1회)
14
-
15
- macOS는 Google Chrome이 필요하다. Chromium이나 다른 브라우저로는 창이 열리지 않는다.
13
+ `ok`가 안 나오면 macOS는 Google Chrome이 필요하다. Chromium이나 다른 브라우저로는 창이 열리지 않는다.
16
14
 
17
15
  ```bash
18
16
  brew install --cask google-chrome
@@ -20,6 +18,39 @@ brew install --cask google-chrome
20
18
 
21
19
  또는 https://www.google.com/chrome/ 에서 내려받는다.
22
20
 
21
+ ## 2. 동작 확인
22
+
23
+ 동봉된 예제로 창 띄우기와 PNG 내보내기까지 한 번에 확인한다. 아래 `SKILL_DIR`은 이 문서가 들어 있는 스킬 디렉터리의 절대 경로다.
24
+
25
+ ```bash
26
+ SKILL_DIR=<이 스킬 디렉터리>
27
+ cp "$SKILL_DIR/assets/examples/flowchart.excalidraw" /tmp/excal-check.excalidraw
28
+ node "$SKILL_DIR/scripts/excal.mjs" open /tmp/excal-check.excalidraw
29
+ node "$SKILL_DIR/scripts/excal.mjs" snapshot /tmp/excal-check.excalidraw -o /tmp/excal-check.png
30
+ ```
31
+
32
+ - `open` 출력의 `"window"`가 `"opened"`이고 주문 처리 흐름도가 뜬 Chrome 창이 보이면 정상이다.
33
+ - `snapshot`이 `/tmp/excal-check.png`를 출력하면 내보내기까지 된 것이다. 이미지를 열어 한글 라벨이 깨지지 않았는지 본다.
34
+ - `"window"`가 `"no-browser"`면 1번 단계로 돌아간다. `"not-connected"`면 Chrome은 있는데 창이 안 뜬 것이니 열린 Chrome 창과 다른 데스크톱(Space)을 확인한다.
35
+
36
+ 확인이 끝나면 정리한다.
37
+
38
+ ```bash
39
+ node "$SKILL_DIR/scripts/excal.mjs" stop
40
+ rm -f /tmp/excal-check.excalidraw /tmp/excal-check.png
41
+ ```
42
+
43
+ ## 앱을 직접 빌드할 때
44
+
45
+ `build`는 `app/` 소스를 고쳤을 때만 쓴다. 이 레포 안에서 쓰는 경우 `app/`은 pnpm 워크스페이스 글롭(`packages/*`)에 걸리지 않는 비멤버라서, 설치할 때 `--ignore-workspace`가 없으면 상위 워크스페이스가 대신 설치되고 `app/node_modules`가 비어 빌드가 깨진다. `excal build`는 이 플래그를 알아서 붙인다. 손으로 돌릴 때는 이렇게 한다.
46
+
47
+ ```bash
48
+ pnpm --dir "$SKILL_DIR/app" install --ignore-workspace --frozen-lockfile
49
+ pnpm --dir "$SKILL_DIR/app" run build
50
+ ```
51
+
52
+ 빌드에는 Node `^20.19 || >=22.12`가 필요하다(vite 8). 스킬을 쓰기만 할 때는 Node 18 이상이면 된다.
53
+
23
54
  ## 참고
24
55
 
25
56
  - 창은 사용자 Chrome 프로필과 분리된 전용 프로필(`~/.cache/pi-excalidraw/chrome-profile`)로 열린다. 로그인 정보나 확장 프로그램은 공유되지 않는다.
@@ -18,15 +18,15 @@
18
18
 
19
19
  ## 크기와 간격 (20px 그리드 기준)
20
20
 
21
- - 일반 노드 `200×72`, 긴 라벨 노드 `240×80`, 판단 다이아몬드 `220×120`, 시작/끝 타원 `160×64`.
21
+ - 일반 노드 `200×72`, 긴 라벨 노드 `240×80`, 판단 다이아몬드 `240×120`(한글 5자까지. 더 길면 `280×140`), 시작/끝 타원 `160×64`.
22
22
  - 라벨 폰트 20, 제목 28–36, 보조 설명 16.
23
- - 라벨 폭 추정: 한글 한 글자 ≈ fontSize, 영문 ≈ 0.55×fontSize. 좌우 여백 48px를 더한 값이 `width`보다 크면 너비를 늘리거나 `\n`으로 줄을 나눈다. 높이는 줄 수 × fontSize × 1.25 + 32px 이상, 다이아몬드는 모서리가 좁으니 1.5배로 잡는다. 실제 폰트는 추정보다 넓게 그려지므로 빠듯하게 맞추지 않는다.
23
+ - 라벨 폭 추정: 한글 한 글자 ≈ fontSize, 영문 ≈ 0.55×fontSize. 좌우 여백 48px를 더한 값이 `width`보다 크면 너비를 늘리거나 `\n`으로 줄을 나눈다. 높이는 줄 수 × fontSize × 1.25 + 32px 이상, 다이아몬드는 모서리가 좁으니 가로·세로 모두 1.5배로 잡는다. `lint`도 같은 식으로 검사하므로 경고가 뜨면 폭을 20px 단위로 올린다. 실제 폰트는 추정보다 넓게 그려지므로 빠듯하게 맞추지 않는다.
24
24
  - 노드 사이 간격: 라벨 없는 화살표 60px 이상, 라벨 있는 화살표 120px 이상(라벨이 화살표 위에 올라간다).
25
25
  - 좌표는 20의 배수로 맞춘다. 같은 열의 노드는 `x`를, 같은 행은 `y`를 똑같이 둔다.
26
26
 
27
27
  ## 레시피
28
28
 
29
- **세로 플로우차트**: 한 열에 `x` 고정, 노드 높이 72 + 간격 60 → 행 간격 140(라벨 화살표가 있으면 180). 분기는 다이아몬드 오른쪽으로 320px 떨어진 열에 둔다. 제목은 맨 위 `y=20`.
29
+ **세로 플로우차트**: 한 열에 `x` 고정, 노드 높이 72 + 간격 60 → 행 간격 140(라벨 화살표가 있으면 180). 분기는 다이아몬드 오른쪽으로 320px 떨어진 열에 둔다. 제목은 맨 위 `y=20`. 완성 예시는 `assets/examples/flowchart.excalidraw`에 있다.
30
30
 
31
31
  **가로 파이프라인**: 한 행에 `y` 고정, 노드 폭 200 + 간격 100 → 열 간격 300. 5단계를 넘으면 두 줄로 접고 줄 끝에서 꺾인 화살표로 잇는다.
32
32
 
@@ -27,7 +27,7 @@
27
27
  1. `pendingMermaid`가 있으면 mermaid 결과로 요소 전체 교체.
28
28
  2. 스켈레톤 정규화(`convertMixed`). 정식 frame에는 `children: []`를 넣어 변환기 크래시를 막는다.
29
29
  3. `repairScene`: 도형 이동에 따른 직선 화살표 재계산, 바인딩 역참조 보강.
30
- 4. `restoreElements(remote, local, {refreshDimensions, repairBindings})`: 텍스트 재측정, 끊긴 바인딩 제거.
30
+ 4. `loadTextFonts`로 씬의 텍스트 글리프를 `document.fonts.load`로 받은 뒤 `restoreElements(remote, local, {refreshDimensions, repairBindings})`: 텍스트 재측정, 끊긴 바인딩 제거.
31
31
  5. `recenterLabels`: 재측정된 크기로 라벨을 도형 안에 다시 정렬.
32
32
  6. 저장 안 된 로컬 변경이 있으면 `threeWayMerge`.
33
33
  7. 파일에서 사라진 요소는 `isDeleted` tombstone으로, 내용이 바뀐 요소는 `version+1`·새 `versionNonce`로 만든다. Excalidraw Store는 versionNonce로 변경을 감지하므로 이게 없으면 LLM 변경이 히스토리에 안 남고, 이후 Cmd+Z가 LLM 이전 값으로 되감긴다.
@@ -35,7 +35,7 @@
35
35
  9. 1–5단계에서 내용이 바뀌었거나 병합으로 로컬 변경이 남았으면 곧바로 파일에 다시 저장한다.
36
36
  - 3-way 기준선: 마지막 동기화 시점의 요소별 `{version, contentKey}`. 로컬만 바뀐 요소는 로컬, 원격이 바뀐 요소는 원격이 이긴다.
37
37
  - 로컬 편집 저장: `onChange`에서 요소 version 합이 바뀐 경우만 400ms debounce 후 PUT.
38
- - 스냅샷: CLI → POST `/snapshot` → 서버가 첫 번째 연결 창에 `snapshot {reqId, rev}` → 창이 해당 rev 반영까지 최대 5초 대기 후 `exportToBlob` → POST `/api/snapshots/<reqId>` → CLI가 PNG 저장. 창이 그 rev를 반영하지 못했으면(마지막 반영 오류 포함) PNG 대신 에러 JSON을 보내고 CLI는 실패로 끝난다.
38
+ - 스냅샷: CLI → POST `/snapshot` → 서버가 첫 번째 연결 창에 `snapshot {reqId, rev}` → 창이 해당 rev 반영까지 최대 5초 대기 → `refitText`(아래 폰트 절) → `exportToBlob` → POST `/api/snapshots/<reqId>` → CLI가 PNG 저장. 창이 그 rev를 반영하지 못했으면(마지막 반영 오류 포함) PNG 대신 에러 JSON을 보내고 CLI는 실패로 끝난다.
39
39
 
40
40
  ## 보안
41
41
 
@@ -45,3 +45,11 @@
45
45
  ## 폰트
46
46
 
47
47
  빌드할 때 `node_modules/@excalidraw/excalidraw/dist/prod/fonts`를 `dist/fonts`로 복사하고 `window.EXCALIDRAW_ASSET_PATH = "/"`로 로컬에서 서빙한다(오프라인 동작, 한글은 Xiaolai 손글씨 대체 폰트). 로컬 로드에 실패하면 Excalidraw가 CDN으로 폴백한다.
48
+
49
+ Xiaolai는 unicode-range 청크 단위로 늦게 로드된다. Excalidraw는 그 순간 쓸 수 있는 폰트로 텍스트 `width`를 재고, 폰트가 도착하면 다시 그리기만 한다(`Fonts.onLoaded`는 shape 캐시만 지우고 크기는 유지). 대체 폰트로 잰 폭이 남으면 창은 텍스트를 그 폭에서 잘라 그리고, 앱은 그 폭을 파일에 저장한다. `exportToBlob`은 새로 그려서 잘림이 PNG에 안 보인다. 그래서 앱은 다음 세 곳에서 폰트를 맞춘다.
50
+
51
+ - 원격 반영 4단계: 재측정 전에 필요한 글리프를 로드한다.
52
+ - `document.fonts`의 `loadingdone`(150ms debounce): `refitText`가 텍스트 요소만 다시 재고 라벨을 재정렬한다. 크기가 바뀌었으면 `captureUpdate: NEVER`로 반영하고 저장한다. 텍스트 편집 중이면 건너뛴다.
53
+ - 스냅샷 직전: 같은 `refitText`를 큐에서 돌려 PNG와 창이 같은 크기를 쓰게 한다.
54
+
55
+ 예전 버전이 좁은 폭을 저장한 파일도 창에서 열면 이 경로로 스스로 고쳐진다.
@@ -42,7 +42,15 @@ function ensureBuilt(force = false) {
42
42
  const pm = spawnSync("pnpm", ["--version"]).status === 0 ? "pnpm" : "npm";
43
43
  if (!fs.existsSync(path.join(APP_DIR, "node_modules"))) {
44
44
  console.error(`[excal] 첫 실행: ${pm} install (1회만)`);
45
- const r = spawnSync(pm, ["install"], { cwd: APP_DIR, stdio: ["ignore", 2, 2] });
45
+ const run = (args) => spawnSync(pm, args, { cwd: APP_DIR, stdio: ["ignore", 2, 2] });
46
+ // In the source repo app/ sits inside a pnpm workspace without being a member, so a plain
47
+ // `pnpm install` installs the workspace instead and leaves app/node_modules empty.
48
+ const pnpmArgs = ["install", "--ignore-workspace"];
49
+ let r = run(pm === "pnpm" ? [...pnpmArgs, "--frozen-lockfile"] : ["install"]);
50
+ if (r.status !== 0 && pm === "pnpm") {
51
+ console.error("[excal] frozen-lockfile 설치 실패(잠금 파일 불일치). 잠금 파일을 갱신해 재시도합니다");
52
+ r = run(pnpmArgs);
53
+ }
46
54
  if (r.status !== 0) die("[excal] install 실패");
47
55
  }
48
56
  console.error("[excal] 앱 빌드 중");
@@ -101,22 +109,29 @@ function chromeCommand(url) {
101
109
  "--window-size=1440,960",
102
110
  ...(process.env.EXCAL_CHROME_ARGS ? process.env.EXCAL_CHROME_ARGS.split(/\s+/).filter(Boolean) : []),
103
111
  ];
104
- if (process.platform === "darwin") return ["open", ["-na", "Google Chrome", "--args", ...args]];
112
+ if (process.platform === "darwin") {
113
+ // `open -na` succeeds silently even when the app is missing, so probe first.
114
+ if (spawnSync("open", ["-Ra", "Google Chrome"]).status !== 0) return null;
115
+ return ["open", ["-na", "Google Chrome", "--args", ...args]];
116
+ }
105
117
  for (const bin of ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser"]) {
106
118
  if (spawnSync("which", [bin]).status === 0) return [bin, args];
107
119
  }
108
120
  return null;
109
121
  }
110
122
 
123
+ /** @returns {"opened"|"not-connected"|"no-browser"|"browser-disabled"} */
111
124
  async function openWindow(srv, info) {
112
- if (process.env.EXCAL_BROWSER === "none") return false;
125
+ if (process.env.EXCAL_BROWSER === "none") return "browser-disabled";
113
126
  const cmd = chromeCommand(info.url);
114
127
  if (!cmd) {
115
- console.error(`[excal] Chrome을 찾지 못했습니다. 직접 여세요: ${info.url}`);
116
- return false;
128
+ console.error(
129
+ `[excal] Chrome을 찾지 못했습니다. references/setup.md의 설치 방법을 사용자에게 안내하세요. 직접 열려면: ${info.url}`,
130
+ );
131
+ return "no-browser";
117
132
  }
118
133
  spawn(cmd[0], cmd[1], { detached: true, stdio: "ignore" }).unref();
119
- return waitForClient(srv, info.id, 20_000);
134
+ return (await waitForClient(srv, info.id, 20_000)) ? "opened" : "not-connected";
120
135
  }
121
136
 
122
137
  async function waitForClient(srv, id, timeoutMs) {
@@ -169,6 +184,23 @@ function textWidthEstimate(text, fontSize = 20) {
169
184
  return max;
170
185
  }
171
186
 
187
+ /** Bounding box of an element, estimating text size the window has not measured yet. */
188
+ function measuredBox(e) {
189
+ if (typeof e.x !== "number" || typeof e.y !== "number") return null;
190
+ const text = String(e.text ?? "");
191
+ const fontSize = e.fontSize ?? 20;
192
+ const w =
193
+ typeof e.width === "number" ? Math.abs(e.width) : e.type === "text" ? textWidthEstimate(text, fontSize) : undefined;
194
+ const h =
195
+ typeof e.height === "number"
196
+ ? Math.abs(e.height)
197
+ : e.type === "text"
198
+ ? fontSize * 1.25 * text.split("\n").length
199
+ : undefined;
200
+ if (w === undefined && h === undefined) return null;
201
+ return { x: e.x, y: e.y, w: w ?? 0, h: h ?? 0 };
202
+ }
203
+
172
204
  function labelOf(el, byId) {
173
205
  if (el.label?.text) return el.label.text;
174
206
  if (el.type === "text") return el.text;
@@ -188,11 +220,11 @@ function inspect(file) {
188
220
  const byId = new Map(elements.map((e) => [e.id, e]));
189
221
  const visible = elements.filter((e) => !(e.type === "text" && e.containerId));
190
222
  const count = (pred) => visible.filter(pred).length;
191
- const boxes = visible.filter((e) => typeof e.x === "number" && typeof e.width === "number");
192
- const minX = Math.min(...boxes.map((e) => e.x));
193
- const minY = Math.min(...boxes.map((e) => e.y));
194
- const maxX = Math.max(...boxes.map((e) => e.x + Math.abs(e.width)));
195
- const maxY = Math.max(...boxes.map((e) => e.y + Math.abs(e.height ?? 0)));
223
+ const boxes = visible.map(measuredBox).filter(Boolean);
224
+ const minX = Math.min(...boxes.map((b) => b.x));
225
+ const minY = Math.min(...boxes.map((b) => b.y));
226
+ const maxX = Math.max(...boxes.map((b) => b.x + b.w));
227
+ const maxY = Math.max(...boxes.map((b) => b.y + b.h));
196
228
  console.log(
197
229
  `${path.basename(file)} 요소 ${visible.length}개 (도형 ${count((e) => SHAPES.has(e.type))}, 화살표/선 ${count((e) => LINEAR.has(e.type))}, 텍스트 ${count((e) => e.type === "text")})` +
198
230
  (boxes.length ? ` 범위 x ${round(minX)}~${round(maxX)}, y ${round(minY)}~${round(maxY)}` : "") +
@@ -276,23 +308,10 @@ function lint(file) {
276
308
  }
277
309
  // partial overlaps between top-level boxes (containment is treated as intentional grouping)
278
310
  const boxes = elements
279
- .filter((e) => (SHAPES.has(e.type) || (e.type === "text" && !e.containerId)) && typeof e.x === "number")
311
+ .filter((e) => SHAPES.has(e.type) || (e.type === "text" && !e.containerId))
280
312
  .map((e) => {
281
- const w =
282
- typeof e.width === "number"
283
- ? e.width
284
- : e.type === "text"
285
- ? textWidthEstimate(e.text, e.fontSize ?? 20)
286
- : undefined;
287
- const h =
288
- typeof e.height === "number"
289
- ? e.height
290
- : e.type === "text"
291
- ? (e.fontSize ?? 20) * 1.25 * String(e.text).split("\n").length
292
- : undefined;
293
- return w === undefined || h === undefined
294
- ? null
295
- : { id: e.id ?? e.type, x1: e.x, y1: e.y, x2: e.x + w, y2: e.y + h };
313
+ const b = measuredBox(e);
314
+ return b && { id: e.id ?? e.type, x1: b.x, y1: b.y, x2: b.x + b.w, y2: b.y + b.h };
296
315
  })
297
316
  .filter(Boolean);
298
317
  const contains = (a, b) => a.x1 <= b.x1 && a.y1 <= b.y1 && a.x2 >= b.x2 && a.y2 >= b.y2;
@@ -338,22 +357,34 @@ switch (cmd) {
338
357
  case "open": {
339
358
  const mermaid = flag("--from-mermaid");
340
359
  const noWindow = bool("--no-window");
341
- const file = resolveFile(rest[0], { create: true });
360
+ // Read the mermaid source before resolveFile(create) so a typo does not leave an empty diagram behind.
361
+ let mermaidSrc;
342
362
  if (mermaid) {
343
- const src = fs.readFileSync(path.resolve(mermaid), "utf8");
363
+ const src = path.resolve(mermaid);
364
+ if (!fs.existsSync(src)) die(`[excal] mermaid 파일이 없습니다: ${src}`);
365
+ try {
366
+ mermaidSrc = fs.readFileSync(src, "utf8");
367
+ } catch (err) {
368
+ die(`[excal] mermaid 파일을 읽지 못했습니다: ${src} (${err.message})`);
369
+ }
370
+ }
371
+ const file = resolveFile(rest[0], { create: true });
372
+ if (mermaidSrc !== undefined) {
344
373
  const data = JSON.parse(fs.readFileSync(file, "utf8"));
345
- data.pendingMermaid = src;
374
+ data.pendingMermaid = mermaidSrc;
346
375
  writeFileAtomic(file, JSON.stringify(data, null, 2) + "\n");
347
376
  }
348
377
  const srv = await ensureServer();
349
378
  const info = await register(srv, file);
350
379
  let windowState = "already-open";
351
- if (info.clients === 0) {
352
- windowState = noWindow ? "not-opened" : (await openWindow(srv, info)) ? "opened" : "not-connected";
353
- }
380
+ if (info.clients === 0) windowState = noWindow ? "not-opened" : await openWindow(srv, info);
354
381
  console.log(JSON.stringify({ file, url: info.url, window: windowState }, null, 2));
355
- if (mermaid && windowState !== "not-opened")
356
- console.error("[excal] mermaid 변환은 창에서 수행됩니다. 변환 후 파일이 정식 포맷으로 다시 저장됩니다.");
382
+ if (mermaid)
383
+ console.error(
384
+ windowState === "opened" || windowState === "already-open"
385
+ ? "[excal] mermaid 변환은 창에서 수행됩니다. 변환 후 파일이 정식 포맷으로 다시 저장됩니다."
386
+ : `[excal] 창이 없어 mermaid 변환이 보류됩니다 (window: ${windowState}). 파일에는 pendingMermaid만 남습니다.`,
387
+ );
357
388
  break;
358
389
  }
359
390
  case "inspect":
@@ -367,8 +398,11 @@ switch (cmd) {
367
398
  const file = resolveFile(rest[0]);
368
399
  const srv = await ensureServer();
369
400
  const info = await register(srv, file);
370
- if (info.clients === 0 && !(await openWindow(srv, info)))
371
- die("[excal] 창에 연결하지 못해 스냅샷을 찍을 수 없습니다");
401
+ if (info.clients === 0) {
402
+ const windowState = await openWindow(srv, info);
403
+ if (windowState !== "opened")
404
+ die(`[excal] 창에 연결하지 못해 스냅샷을 찍을 수 없습니다 (window: ${windowState})`);
405
+ }
372
406
  const r = await call(srv.port, srv.token, `/api/files/${info.id}/snapshot`, { method: "POST" });
373
407
  if (!r.ok) die(`[excal] 스냅샷 실패: ${await r.text()}`);
374
408
  const target = out
@@ -0,0 +1,64 @@
1
+ // Browser-free regression tests for excal.mjs. Commands that need the sync server
2
+ // (open with a window, snapshot) are covered by the manual smoke in references/setup.md.
3
+ import assert from "node:assert/strict";
4
+ import { spawnSync } from "node:child_process";
5
+ import fs from "node:fs";
6
+ import os from "node:os";
7
+ import path from "node:path";
8
+ import test from "node:test";
9
+ import { fileURLToPath } from "node:url";
10
+
11
+ const SCRIPTS_DIR = path.dirname(fileURLToPath(import.meta.url));
12
+ const CLI = path.join(SCRIPTS_DIR, "excal.mjs");
13
+ const EXAMPLE = path.join(SCRIPTS_DIR, "..", "assets", "examples", "flowchart.excalidraw");
14
+
15
+ const run = (args, cwd) =>
16
+ spawnSync(process.execPath, [CLI, ...args], { cwd, encoding: "utf8", env: { ...process.env } });
17
+
18
+ const withTempDir = (fn) => {
19
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "excal-test-"));
20
+ try {
21
+ return fn(dir);
22
+ } finally {
23
+ fs.rmSync(dir, { recursive: true, force: true });
24
+ }
25
+ };
26
+
27
+ test("bundled example passes the bundled linter", () => {
28
+ const r = run(["lint", EXAMPLE]);
29
+ assert.equal(r.status, 0, r.stdout + r.stderr);
30
+ assert.match(r.stdout, /^OK$/m);
31
+ });
32
+
33
+ test("open --from-mermaid with a missing file fails without creating the diagram", () => {
34
+ withTempDir((dir) => {
35
+ const target = path.join(dir, "out.excalidraw");
36
+ const r = run(["open", target, "--from-mermaid", path.join(dir, "nope.mmd")], dir);
37
+ assert.equal(r.status, 1);
38
+ assert.match(r.stderr, /mermaid 파일이 없습니다/);
39
+ assert.doesNotMatch(r.stderr, /at .*excal\.mjs/);
40
+ assert.equal(fs.existsSync(target), false);
41
+ });
42
+ });
43
+
44
+ test("inspect bounds cover text elements the window has not measured yet", () => {
45
+ withTempDir((dir) => {
46
+ const file = path.join(dir, "scene.excalidraw");
47
+ fs.writeFileSync(
48
+ file,
49
+ JSON.stringify({
50
+ type: "excalidraw",
51
+ version: 2,
52
+ elements: [
53
+ { type: "text", id: "title", x: 100, y: 20, text: "제목", fontSize: 28 },
54
+ { type: "rectangle", id: "box", x: 400, y: 200, width: 200, height: 72 },
55
+ ],
56
+ appState: {},
57
+ files: {},
58
+ }),
59
+ );
60
+ const r = run(["inspect", file], dir);
61
+ assert.equal(r.status, 0, r.stdout + r.stderr);
62
+ assert.match(r.stdout, /범위 x 100~600, y 20~272/);
63
+ });
64
+ });
@@ -1 +0,0 @@
1
- import"./chunk-FOHPRMQF-DzxwRta7.js";import{M as e}from"./mermaid-parser.core-g7vsDY1V.js";export{e as createArchitectureServices};