@ryan_nookpi/pi-skill-excalidraw 0.1.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 (422) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -0
  3. package/package.json +44 -0
  4. package/skills/excalidraw/SKILL.md +58 -0
  5. package/skills/excalidraw/app/dist/assets/Assistant-Bold-gm-uSS1B.woff2 +0 -0
  6. package/skills/excalidraw/app/dist/assets/Assistant-Medium-DrcxCXg3.woff2 +0 -0
  7. package/skills/excalidraw/app/dist/assets/Assistant-Regular-DVxZuzxb.woff2 +0 -0
  8. package/skills/excalidraw/app/dist/assets/Assistant-SemiBold-SCI4bEL9.woff2 +0 -0
  9. package/skills/excalidraw/app/dist/assets/abnfDiagram-VCTEODGH-Y56Pk8sn.js +1 -0
  10. package/skills/excalidraw/app/dist/assets/ar-SA-G6X2FPQ2-DatABGjX.js +10 -0
  11. package/skills/excalidraw/app/dist/assets/arc-DgcGdgmL.js +1 -0
  12. package/skills/excalidraw/app/dist/assets/architecture-7GRP2DOG-BECp2Zrk.js +1 -0
  13. package/skills/excalidraw/app/dist/assets/architectureDiagram-5GKGNRK7-BkBTZ8aU.js +36 -0
  14. package/skills/excalidraw/app/dist/assets/array-BifhSqXX.js +1 -0
  15. package/skills/excalidraw/app/dist/assets/az-AZ-76LH7QW2-WQrHOob7.js +1 -0
  16. package/skills/excalidraw/app/dist/assets/bg-BG-XCXSNQG7-BVvnFVRX.js +5 -0
  17. package/skills/excalidraw/app/dist/assets/blockDiagram-I7D4REHJ-D7KnLVtF.js +129 -0
  18. package/skills/excalidraw/app/dist/assets/bn-BD-2XOGV67Q-DzlalUE2.js +5 -0
  19. package/skills/excalidraw/app/dist/assets/c4Diagram-7LVT6UL2-nV-MvTJT.js +38 -0
  20. package/skills/excalidraw/app/dist/assets/ca-ES-6MX7JW3Y-ILmKOQy8.js +8 -0
  21. package/skills/excalidraw/app/dist/assets/channel-Ca-ODdO0.js +1 -0
  22. package/skills/excalidraw/app/dist/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
  23. package/skills/excalidraw/app/dist/assets/chunk-4HAMMTFA-CZal2Pyz.js +62 -0
  24. package/skills/excalidraw/app/dist/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
  25. package/skills/excalidraw/app/dist/assets/chunk-75Z2AOVW-DVqFChOg.js +2 -0
  26. package/skills/excalidraw/app/dist/assets/chunk-DU6HZSFF-BavxyZSY.js +127 -0
  27. package/skills/excalidraw/app/dist/assets/chunk-EIO257PC-LlbbXIq6.js +22 -0
  28. package/skills/excalidraw/app/dist/assets/chunk-F27PBJKO-BWDl5_Sz.js +1 -0
  29. package/skills/excalidraw/app/dist/assets/chunk-FOHPRMQF-DzxwRta7.js +161 -0
  30. package/skills/excalidraw/app/dist/assets/chunk-GMAD6QVW-Rjz_G1RQ.js +72 -0
  31. package/skills/excalidraw/app/dist/assets/chunk-GVQU2GXP-BjxGnl2S.js +1 -0
  32. package/skills/excalidraw/app/dist/assets/chunk-IMKFNOWR-BJ1FymPn.js +231 -0
  33. package/skills/excalidraw/app/dist/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
  34. package/skills/excalidraw/app/dist/assets/chunk-L3NEJ4N5-sJejB3eG.js +1 -0
  35. package/skills/excalidraw/app/dist/assets/chunk-OSK3NFVY-DLz3HNrx.js +10 -0
  36. package/skills/excalidraw/app/dist/assets/chunk-P2QGCYS3-Dhy4aMJl.js +1 -0
  37. package/skills/excalidraw/app/dist/assets/chunk-POPQ4Y6H-DOEx_tr4.js +1 -0
  38. package/skills/excalidraw/app/dist/assets/chunk-PWAF6VOD-Bq0yNT01.js +1 -0
  39. package/skills/excalidraw/app/dist/assets/chunk-SHT3W25Y-CICAAbIL.js +168 -0
  40. package/skills/excalidraw/app/dist/assets/chunk-SVP7TREG-f6pYNHg0.js +88 -0
  41. package/skills/excalidraw/app/dist/assets/chunk-TICWLB2K-DdRa5lMo.js +206 -0
  42. package/skills/excalidraw/app/dist/assets/chunk-XXDRQBXY-BvKO0v-W.js +1 -0
  43. package/skills/excalidraw/app/dist/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
  44. package/skills/excalidraw/app/dist/assets/classDiagram-ZZMXUADV-DCPVGkLh.js +1 -0
  45. package/skills/excalidraw/app/dist/assets/classDiagram-v2-VYDZK3BY-DCPVGkLh.js +1 -0
  46. package/skills/excalidraw/app/dist/assets/cose-bilkent-JH36ORCC-COXSdE6a.js +1 -0
  47. package/skills/excalidraw/app/dist/assets/cs-CZ-2BRQDIVT-tV1CvCxu.js +11 -0
  48. package/skills/excalidraw/app/dist/assets/cynefin-OW5HDTMX-D-XvS_F8.js +1 -0
  49. package/skills/excalidraw/app/dist/assets/cynefinDiagram-5FMLGOSQ-DWgEonYW.js +62 -0
  50. package/skills/excalidraw/app/dist/assets/cytoscape.esm-Yq6u8L66.js +321 -0
  51. package/skills/excalidraw/app/dist/assets/da-DK-5WZEPLOC-zceHT2GU.js +5 -0
  52. package/skills/excalidraw/app/dist/assets/dagre-C-rnJpZF.js +1 -0
  53. package/skills/excalidraw/app/dist/assets/dagre-GXQ25YYZ-d3UPf-z-.js +4 -0
  54. package/skills/excalidraw/app/dist/assets/de-DE-XR44H4JA-U1KbS0X3.js +8 -0
  55. package/skills/excalidraw/app/dist/assets/defaultLocale-BFoDCU3G.js +1 -0
  56. package/skills/excalidraw/app/dist/assets/diagram-S7CK7UJ4-CRc82Fb9.js +30 -0
  57. package/skills/excalidraw/app/dist/assets/diagram-UQ7AKVKN-CgfviMBL.js +41 -0
  58. package/skills/excalidraw/app/dist/assets/diagram-VSXAHHWV-Btexo3l8.js +3 -0
  59. package/skills/excalidraw/app/dist/assets/diagram-VX7I27RA-DoMnip1P.js +24 -0
  60. package/skills/excalidraw/app/dist/assets/diagram-Z3DM3KII-BTsxq82z.js +24 -0
  61. package/skills/excalidraw/app/dist/assets/directory-open-01563666-D4xXyWb_.js +1 -0
  62. package/skills/excalidraw/app/dist/assets/directory-open-4ed118d0-1i309Asm.js +1 -0
  63. package/skills/excalidraw/app/dist/assets/dist-B_J_HbC0.js +1 -0
  64. package/skills/excalidraw/app/dist/assets/dist-DVyo1T_H.js +62 -0
  65. package/skills/excalidraw/app/dist/assets/ebnfDiagram-PWID7BFC-BYsNxk9n.js +1 -0
  66. package/skills/excalidraw/app/dist/assets/el-GR-BZB4AONW-CgSV8rfE.js +10 -0
  67. package/skills/excalidraw/app/dist/assets/en-B4ZKOASM-Byd7DbO_.js +1 -0
  68. package/skills/excalidraw/app/dist/assets/erDiagram-RLTQ6QDP-LkewsTFZ.js +99 -0
  69. package/skills/excalidraw/app/dist/assets/es-ES-U4NZUMDT-d42UjG1W.js +9 -0
  70. package/skills/excalidraw/app/dist/assets/eu-ES-A7QVB2H4-CeMeFdnp.js +11 -0
  71. package/skills/excalidraw/app/dist/assets/eventmodeling-NTZA5JFV-160-dgiE.js +1 -0
  72. package/skills/excalidraw/app/dist/assets/fa-IR-HGAKTJCU-CVXIrtPl.js +8 -0
  73. package/skills/excalidraw/app/dist/assets/fi-FI-Z5N7JZ37-DIW6dubX.js +6 -0
  74. package/skills/excalidraw/app/dist/assets/file-open-002ab408-BHUWm0Sh.js +1 -0
  75. package/skills/excalidraw/app/dist/assets/file-open-7c801643-DZtJT5zp.js +1 -0
  76. package/skills/excalidraw/app/dist/assets/file-save-3189631c-CO9S4HFW.js +1 -0
  77. package/skills/excalidraw/app/dist/assets/file-save-745eba88-Bwdfz6OZ.js +1 -0
  78. package/skills/excalidraw/app/dist/assets/flowDiagram-HODETNUW-XE0UaaJt.js +1 -0
  79. package/skills/excalidraw/app/dist/assets/fr-FR-RHASNOE6-EPyEt_4g.js +9 -0
  80. package/skills/excalidraw/app/dist/assets/ganttDiagram-EL5Y4UJY-DuJaTNOL.js +292 -0
  81. package/skills/excalidraw/app/dist/assets/gitGraph-4MIJSDKK-DHGj5WCj.js +1 -0
  82. package/skills/excalidraw/app/dist/assets/gitGraphDiagram-WWUBYQGX-DdvnVTzU.js +106 -0
  83. package/skills/excalidraw/app/dist/assets/gl-ES-HMX3MZ6V-Cbr9hGRI.js +10 -0
  84. package/skills/excalidraw/app/dist/assets/graphlib-DS17s2tU.js +1 -0
  85. package/skills/excalidraw/app/dist/assets/he-IL-6SHJWFNN-B5Iboi8a.js +10 -0
  86. package/skills/excalidraw/app/dist/assets/hi-IN-IWLTKZ5I-k6swWOWn.js +4 -0
  87. package/skills/excalidraw/app/dist/assets/hu-HU-A5ZG7DT2-DaDlVvTE.js +7 -0
  88. package/skills/excalidraw/app/dist/assets/id-ID-SAP4L64H-DiV3qfW7.js +10 -0
  89. package/skills/excalidraw/app/dist/assets/image-GAAHSSAO-stUGVFAH.js +1 -0
  90. package/skills/excalidraw/app/dist/assets/image-blob-reduce.esm-BFq-Mx5V.js +2 -0
  91. package/skills/excalidraw/app/dist/assets/index-ChSm4O-k.js +208 -0
  92. package/skills/excalidraw/app/dist/assets/index-DtRVLazd.css +1 -0
  93. package/skills/excalidraw/app/dist/assets/info-A6RAGUB7-EjytcsrH.js +1 -0
  94. package/skills/excalidraw/app/dist/assets/infoDiagram-27XIBGKW-D5p952K8.js +2 -0
  95. package/skills/excalidraw/app/dist/assets/init-C-OQMol4.js +1 -0
  96. package/skills/excalidraw/app/dist/assets/ishikawaDiagram-5VMMS53U-Cl9GrB8p.js +70 -0
  97. package/skills/excalidraw/app/dist/assets/it-IT-JPQ66NNP-B3YDn8c7.js +11 -0
  98. package/skills/excalidraw/app/dist/assets/ja-JP-DBVTYXUO-r8GJRlz8.js +8 -0
  99. package/skills/excalidraw/app/dist/assets/journeyDiagram-3NMN7TZE-DPL61tuu.js +139 -0
  100. package/skills/excalidraw/app/dist/assets/kaa-6HZHGXH3-CQXO0smT.js +1 -0
  101. package/skills/excalidraw/app/dist/assets/kab-KAB-ZGHBKWFO-CcnpslfP.js +8 -0
  102. package/skills/excalidraw/app/dist/assets/kanban-definition-UXKFOSKX-CrEwB5Tt.js +89 -0
  103. package/skills/excalidraw/app/dist/assets/katex-ZlcWpGUi.js +257 -0
  104. package/skills/excalidraw/app/dist/assets/kk-KZ-P5N5QNE5-DRBBwSLY.js +1 -0
  105. package/skills/excalidraw/app/dist/assets/km-KH-HSX4SM5Z-aDqmSt43.js +11 -0
  106. package/skills/excalidraw/app/dist/assets/ko-KR-MTYHY66A-CrTedvVS.js +9 -0
  107. package/skills/excalidraw/app/dist/assets/ku-TR-6OUDTVRD-hNhm9YQ0.js +9 -0
  108. package/skills/excalidraw/app/dist/assets/line-C4N-NEvZ.js +1 -0
  109. package/skills/excalidraw/app/dist/assets/linear-BUnzfXgk.js +1 -0
  110. package/skills/excalidraw/app/dist/assets/lt-LT-XHIRWOB4-HeBwh7CL.js +3 -0
  111. package/skills/excalidraw/app/dist/assets/lv-LV-5QDEKY6T-DXU4B6YW.js +7 -0
  112. package/skills/excalidraw/app/dist/assets/mermaid-parser.core-g7vsDY1V.js +7 -0
  113. package/skills/excalidraw/app/dist/assets/mindmap-definition-YA3MSWOX-D__pf1Yg.js +96 -0
  114. package/skills/excalidraw/app/dist/assets/mr-IN-CRQNXWMA-BYsXnQkb.js +13 -0
  115. package/skills/excalidraw/app/dist/assets/my-MM-5M5IBNSE-C871ZDtQ.js +1 -0
  116. package/skills/excalidraw/app/dist/assets/nb-NO-T6EIAALU-Cm-d6hjp.js +10 -0
  117. package/skills/excalidraw/app/dist/assets/nl-NL-IS3SIHDZ-XlSpYp1l.js +8 -0
  118. package/skills/excalidraw/app/dist/assets/nn-NO-6E72VCQL-BJPmp-_h.js +8 -0
  119. package/skills/excalidraw/app/dist/assets/oc-FR-POXYY2M6-D1rPi0z7.js +8 -0
  120. package/skills/excalidraw/app/dist/assets/ordinal-BDEzSJ7C.js +1 -0
  121. package/skills/excalidraw/app/dist/assets/pa-IN-N4M65BXN-hKsyWK74.js +4 -0
  122. package/skills/excalidraw/app/dist/assets/packet-AYTQ26CC-4PAjvWSX.js +1 -0
  123. package/skills/excalidraw/app/dist/assets/path-fybaL0A-.js +1 -0
  124. package/skills/excalidraw/app/dist/assets/pegDiagram-XKGWAZYB-CUfmvI5h.js +1 -0
  125. package/skills/excalidraw/app/dist/assets/percentages-BXMCSKIN-CfhMReVO.js +1 -0
  126. package/skills/excalidraw/app/dist/assets/pica-BPrp-GK1.js +2 -0
  127. package/skills/excalidraw/app/dist/assets/pie-WAS4IAKB-B5FgQybZ.js +1 -0
  128. package/skills/excalidraw/app/dist/assets/pieDiagram-E7YTZNPT-CU8w4Jjl.js +39 -0
  129. package/skills/excalidraw/app/dist/assets/pl-PL-T2D74RX3-DOodWdkP.js +9 -0
  130. package/skills/excalidraw/app/dist/assets/pt-BR-5N22H2LF-dyjRDPfa.js +9 -0
  131. package/skills/excalidraw/app/dist/assets/pt-PT-UZXXM6DQ-DXb9iBoj.js +9 -0
  132. package/skills/excalidraw/app/dist/assets/quadrantDiagram-AXDQQJYC-C60Jl0ho.js +7 -0
  133. package/skills/excalidraw/app/dist/assets/radar-RG4KPBEZ-Bb6LV2iq.js +1 -0
  134. package/skills/excalidraw/app/dist/assets/railroad-74A4TZTK-DYFjVugV.js +1 -0
  135. package/skills/excalidraw/app/dist/assets/railroad-abnf-HS5TGJTU-CCpAO9jj.js +1 -0
  136. package/skills/excalidraw/app/dist/assets/railroad-ebnf-LZEXJU2U-BTtgE2v0.js +1 -0
  137. package/skills/excalidraw/app/dist/assets/railroad-peg-WCYAUIDC-qT7-4WNw.js +1 -0
  138. package/skills/excalidraw/app/dist/assets/railroadDiagram-O6MQD6OU-D-Q-2PY3.js +1 -0
  139. package/skills/excalidraw/app/dist/assets/requirementDiagram-BXWQKSXE-DUJGchGn.js +84 -0
  140. package/skills/excalidraw/app/dist/assets/ro-RO-JPDTUUEW-D2t-SdK7.js +11 -0
  141. package/skills/excalidraw/app/dist/assets/rolldown-runtime-Dd_uD5pT.js +1 -0
  142. package/skills/excalidraw/app/dist/assets/rough.esm-Dy-Kn_BL.js +1 -0
  143. package/skills/excalidraw/app/dist/assets/roundRect-D01gJrlt.js +1 -0
  144. package/skills/excalidraw/app/dist/assets/ru-RU-B4JR7IUQ-B3zBvqM8.js +9 -0
  145. package/skills/excalidraw/app/dist/assets/sankeyDiagram-P5KCCOFB-B7gzsCzv.js +40 -0
  146. package/skills/excalidraw/app/dist/assets/sequenceDiagram-WJ2MYXX4-C1gETguU.js +162 -0
  147. package/skills/excalidraw/app/dist/assets/si-LK-N5RQ5JYF-x9atOM2o.js +1 -0
  148. package/skills/excalidraw/app/dist/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
  149. package/skills/excalidraw/app/dist/assets/sk-SK-C5VTKIMK-Dc-U7HwG.js +6 -0
  150. package/skills/excalidraw/app/dist/assets/sl-SI-NN7IZMDC-DBErI-th.js +6 -0
  151. package/skills/excalidraw/app/dist/assets/src-oBChb5qS.js +1 -0
  152. package/skills/excalidraw/app/dist/assets/stateDiagram-D77RDMKH-ms90ttRw.js +1 -0
  153. package/skills/excalidraw/app/dist/assets/stateDiagram-v2-MP3YSRHH-BLT-8Bst.js +1 -0
  154. package/skills/excalidraw/app/dist/assets/subset-shared.chunk-JkuiVUYR.js +1 -0
  155. package/skills/excalidraw/app/dist/assets/subset-worker.chunk-BBqoITZT.js +1 -0
  156. package/skills/excalidraw/app/dist/assets/sv-SE-XGPEYMSR-DIhT7VhP.js +10 -0
  157. package/skills/excalidraw/app/dist/assets/swimlanes-42K2YHIH-C03UW4hY.js +1 -0
  158. package/skills/excalidraw/app/dist/assets/swimlanesDiagram-VR7AAH4N-OUQB4gXu.js +8 -0
  159. package/skills/excalidraw/app/dist/assets/ta-IN-2NMHFXQM-ollyGw17.js +9 -0
  160. package/skills/excalidraw/app/dist/assets/th-TH-HPSO5L25-GycTdvha.js +2 -0
  161. package/skills/excalidraw/app/dist/assets/timeline-definition-24CTP7MA-BWQ2CWiF.js +120 -0
  162. package/skills/excalidraw/app/dist/assets/tr-TR-DEFEU3FU-C0EQI1hS.js +7 -0
  163. package/skills/excalidraw/app/dist/assets/treeView-Q6P3EWNA-B4gjxheB.js +1 -0
  164. package/skills/excalidraw/app/dist/assets/treemap-WGGIJYW6-C6ZNbWbw.js +1 -0
  165. package/skills/excalidraw/app/dist/assets/uk-UA-QMV73CPH--gALSA3R.js +6 -0
  166. package/skills/excalidraw/app/dist/assets/vennDiagram-4TSXK5OY-DB2q0NWm.js +34 -0
  167. package/skills/excalidraw/app/dist/assets/vi-VN-M7AON7JQ-BU-don7N.js +5 -0
  168. package/skills/excalidraw/app/dist/assets/wardley-WFR3VGLG-CMlUPGFV.js +1 -0
  169. package/skills/excalidraw/app/dist/assets/wardleyDiagram-VM6X3IG4-D7s4KunH.js +78 -0
  170. package/skills/excalidraw/app/dist/assets/xychartDiagram-S5SC5T6Z-C3Ka4xi8.js +7 -0
  171. package/skills/excalidraw/app/dist/assets/zh-CN-LNUGB5OW-D-2E446q.js +10 -0
  172. package/skills/excalidraw/app/dist/assets/zh-HK-E62DVLB3-cftaz5Rd.js +1 -0
  173. package/skills/excalidraw/app/dist/assets/zh-TW-RAJ6MFWO-BO55Yqh9.js +9 -0
  174. package/skills/excalidraw/app/dist/fonts/Assistant/Assistant-Bold.woff2 +0 -0
  175. package/skills/excalidraw/app/dist/fonts/Assistant/Assistant-Medium.woff2 +0 -0
  176. package/skills/excalidraw/app/dist/fonts/Assistant/Assistant-Regular.woff2 +0 -0
  177. package/skills/excalidraw/app/dist/fonts/Assistant/Assistant-SemiBold.woff2 +0 -0
  178. package/skills/excalidraw/app/dist/fonts/Cascadia/CascadiaCode-Regular.woff2 +0 -0
  179. package/skills/excalidraw/app/dist/fonts/ComicShanns/ComicShanns-Regular-279a7b317d12eb88de06167bd672b4b4.woff2 +0 -0
  180. package/skills/excalidraw/app/dist/fonts/ComicShanns/ComicShanns-Regular-6e066e8de2ac57ea9283adb9c24d7f0c.woff2 +0 -0
  181. package/skills/excalidraw/app/dist/fonts/ComicShanns/ComicShanns-Regular-dc6a8806fa96795d7b3be5026f989a17.woff2 +0 -0
  182. package/skills/excalidraw/app/dist/fonts/ComicShanns/ComicShanns-Regular-fcb0fc02dcbee4c9846b3e2508668039.woff2 +0 -0
  183. package/skills/excalidraw/app/dist/fonts/Excalifont/Excalifont-Regular-349fac6ca4700ffec595a7150a0d1e1d.woff2 +0 -0
  184. package/skills/excalidraw/app/dist/fonts/Excalifont/Excalifont-Regular-3f2c5db56cc93c5a6873b1361d730c16.woff2 +0 -0
  185. package/skills/excalidraw/app/dist/fonts/Excalifont/Excalifont-Regular-41b173a47b57366892116a575a43e2b6.woff2 +0 -0
  186. package/skills/excalidraw/app/dist/fonts/Excalifont/Excalifont-Regular-623ccf21b21ef6b3a0d87738f77eb071.woff2 +0 -0
  187. package/skills/excalidraw/app/dist/fonts/Excalifont/Excalifont-Regular-a88b72a24fb54c9f94e3b5fdaa7481c9.woff2 +0 -0
  188. package/skills/excalidraw/app/dist/fonts/Excalifont/Excalifont-Regular-b9dcf9d2e50a1eaf42fc664b50a3fd0d.woff2 +0 -0
  189. package/skills/excalidraw/app/dist/fonts/Excalifont/Excalifont-Regular-be310b9bcd4f1a43f571c46df7809174.woff2 +0 -0
  190. package/skills/excalidraw/app/dist/fonts/Liberation/LiberationSans-Regular.woff2 +0 -0
  191. package/skills/excalidraw/app/dist/fonts/Lilita/Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYE98RXi4EwSsbg.woff2 +0 -0
  192. package/skills/excalidraw/app/dist/fonts/Lilita/Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYEF8RXi4EwQ.woff2 +0 -0
  193. package/skills/excalidraw/app/dist/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTA3j6zbXWjgevT5.woff2 +0 -0
  194. package/skills/excalidraw/app/dist/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTQ3j6zbXWjgeg.woff2 +0 -0
  195. package/skills/excalidraw/app/dist/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTk3j6zbXWjgevT5.woff2 +0 -0
  196. package/skills/excalidraw/app/dist/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTo3j6zbXWjgevT5.woff2 +0 -0
  197. package/skills/excalidraw/app/dist/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTs3j6zbXWjgevT5.woff2 +0 -0
  198. package/skills/excalidraw/app/dist/fonts/Virgil/Virgil-Regular.woff2 +0 -0
  199. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-019d66dcad46dc156b162d267f981c20.woff2 +0 -0
  200. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-04b718e5623574919c8b0dea5f301444.woff2 +0 -0
  201. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-069e77aac84590e2e991d0a0176d34f2.woff2 +0 -0
  202. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-06c77b8c66e51ed6c63ccb502dd8b8af.woff2 +0 -0
  203. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-08e0dc436ad0ad61ba5558db0674d762.woff2 +0 -0
  204. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-093b9ef39a46ceae95a1df18a0a3a326.woff2 +0 -0
  205. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-095c169f3314805276f603a362766abd.woff2 +0 -0
  206. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-09850c4077f3fffe707905872e0e2460.woff2 +0 -0
  207. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-0986d134c05864f5025962eef9f994a0.woff2 +0 -0
  208. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-0b5d723fdc4e249c140f0909e87d03b4.woff2 +0 -0
  209. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-0f626226ba1272e832aea87bafd9720e.woff2 +0 -0
  210. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-0f7fb1e0d5015bb1371343153ecf7ce3.woff2 +0 -0
  211. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-0fa55a080fcd0f9dc2e0b0058b793df8.woff2 +0 -0
  212. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-0facdf1ea213ba40261022f5d5ed4493.woff2 +0 -0
  213. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-10a7ae9a371830a80c3d844acf1c02d7.woff2 +0 -0
  214. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-112c051027b2d766c19a519f6ee1f4f7.woff2 +0 -0
  215. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-11c345711937f0ba4b8f7b6b919c8440.woff2 +0 -0
  216. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-12b52b58eb3df36804b9a654ec9ee194.woff2 +0 -0
  217. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-13ae07ed2e272d26d59bc0691cd7117a.woff2 +0 -0
  218. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-13d2887ec8ee73c43acdabc52a05af7b.woff2 +0 -0
  219. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-145aa02cdd91946e67dc934e1acffe75.woff2 +0 -0
  220. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-15dc6d811c9cd078f9086a740d5a1038.woff2 +0 -0
  221. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-173945821411c09f70c95f98d590e697.woff2 +0 -0
  222. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-1b611157cd46bb184d4fa4dae2d6a2b8.woff2 +0 -0
  223. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-1e6fd68f1f3902ce48ce8c69df385622.woff2 +0 -0
  224. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-1ee544f0f1dac422545c505baa788992.woff2 +0 -0
  225. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-1fdc0c67ed57263a80fd108c1f6ccf24.woff2 +0 -0
  226. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-203b0e569e3b14aac86a003dc3fa523e.woff2 +0 -0
  227. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-20cc1bbf50e7efb442756cb605672c1f.woff2 +0 -0
  228. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-20e7bf72fa05de9adf7dbcc7bf51dde6.woff2 +0 -0
  229. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-21430ee05a1248901da8d0de08744d47.woff2 +0 -0
  230. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-23686f7f29da6e8008c36dd3a80c83d6.woff2 +0 -0
  231. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-23ad2d71b280f00b1363b95b7bea94eb.woff2 +0 -0
  232. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-23f228f3999c01983860012330e4be08.woff2 +0 -0
  233. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-24476a126f129212beb33f66853ea151.woff2 +0 -0
  234. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-24a21c1e4449222e8d1898d69ff3a404.woff2 +0 -0
  235. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-25b7f38e18f035f96cb5e547bd2bd08c.woff2 +0 -0
  236. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-29cec36cd205b211da97acabaa62f055.woff2 +0 -0
  237. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-2a26d20a23b00898ce82f09d2ee47c3f.woff2 +0 -0
  238. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-2adbc89c11e65905393d3dfc468b9d5b.woff2 +0 -0
  239. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-2b7441d46298788ac94e610ffcc709b6.woff2 +0 -0
  240. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-2b77e8ebfb2367ab2662396a60e7d320.woff2 +0 -0
  241. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-2cf96d082d35ea3d8106851223ad0d16.woff2 +0 -0
  242. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-2d43040e86ff03ba677f6f9c04cd0805.woff2 +0 -0
  243. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-2e33e8dc771ef5e1d9127d60a6b73679.woff2 +0 -0
  244. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-33432927cd87d40cfe393c7482bf221f.woff2 +0 -0
  245. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-353f33792a8f60dc69323ddf635a269e.woff2 +0 -0
  246. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-36925dfe329a45086cbb7fc5c20d45ac.woff2 +0 -0
  247. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-3717077e38f98d89eae729b6c14e56dc.woff2 +0 -0
  248. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-3756e81d3e149cf6099163ee79944fec.woff2 +0 -0
  249. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-395c35dd584b56b0789f58a0559beaf1.woff2 +0 -0
  250. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-3c9de2ae0ea4bc91a510942dfa4be8d2.woff2 +0 -0
  251. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-3cc70dbb64df5b21f1326cc24dee2195.woff2 +0 -0
  252. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-3e1f8f654357353bf0e04ba5c34b5f7f.woff2 +0 -0
  253. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-3e63ed8162808a9e425ed80a8bc79114.woff2 +0 -0
  254. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-3eaa538115d76932653c21d8dc28f207.woff2 +0 -0
  255. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-4095eb84ef3874e2600247bee0b04026.woff2 +0 -0
  256. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-41521fade99856108931b4768b1b2648.woff2 +0 -0
  257. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-450da755d5bcb70906e1295e559b9602.woff2 +0 -0
  258. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-452225341522a7942f0f6aab1a5c91a3.woff2 +0 -0
  259. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-4535823663ad81405188a528d8f2b1a2.woff2 +0 -0
  260. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-4806e761d750087c2d734fc64596eaff.woff2 +0 -0
  261. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-4a0fdb40036e87b40aa08dd30584cb85.woff2 +0 -0
  262. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-4a38cc3e9cf104e69ba246d37f8cf135.woff2 +0 -0
  263. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-4aca6a43e59aceee2166b0c7e4e85ef1.woff2 +0 -0
  264. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-4bfaa8ffa64c5ee560aa2daba7c9cbd3.woff2 +0 -0
  265. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-4dc6d5f188d5c96d44815cd1e81aa885.woff2 +0 -0
  266. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-4ddc14ed3eb0c3e46364317dfc0144a3.woff2 +0 -0
  267. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-4ee10ae43505e2e0bc62656ced49c0fa.woff2 +0 -0
  268. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-4f50e5136e136527280bc902c5817561.woff2 +0 -0
  269. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-51502f1206be09c565f1547c406e9558.woff2 +0 -0
  270. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-51a0e808bbc8361236ac521a119758a3.woff2 +0 -0
  271. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-52a84a22fd1369bffeaf21da2d6158dc.woff2 +0 -0
  272. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-5330a2119a716e4e7224ed108b085dac.woff2 +0 -0
  273. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-543fa46ace099a7099dad69123399400.woff2 +0 -0
  274. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-544fc28abe2c5c30e62383fd4dac255f.woff2 +0 -0
  275. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-54acdfc2166ad7fcbd074f75fd4a56ba.woff2 +0 -0
  276. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-5572b3513ba8df57a3d5d7303ee6b11b.woff2 +0 -0
  277. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-56467a5c8840c4d23a60b2f935114848.woff2 +0 -0
  278. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-56a32a7689abd0326e57c10c6c069bb4.woff2 +0 -0
  279. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-57862b464a55b18c7bf234ce22907d73.woff2 +0 -0
  280. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-583d166e56ba0de4b77eabb47ef67839.woff2 +0 -0
  281. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-5882ffa04f32584d26109137e2da4352.woff2 +0 -0
  282. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-58fd02350d0bc52cf1ca3bb32ce9766e.woff2 +0 -0
  283. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-5935a5775af3d5c6307ac667bd9ae74e.woff2 +0 -0
  284. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-59e9ff77b0efaf684bc09274fb6908c9.woff2 +0 -0
  285. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-5a1ce3117cfe90c48e8fb4a9a00f694d.woff2 +0 -0
  286. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-5a45d991244d4c7140217e1e5f5ca4f4.woff2 +0 -0
  287. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-5a7fac4b8b23a6e4e5ba0c9bf1756c91.woff2 +0 -0
  288. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-5b0ed6971aaab9c8ad563230bd5471a7.woff2 +0 -0
  289. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-5d2898fbc097a7e24c6f38d80587621e.woff2 +0 -0
  290. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-603aefd23e350ba7eb124273e3c9bcf1.woff2 +0 -0
  291. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-60a3089806700d379f11827ee9843b6b.woff2 +0 -0
  292. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-60a41c7e1c68f22424e6d22df544bc82.woff2 +0 -0
  293. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-642b26e2e5f5fb780b51b593dbc8c851.woff2 +0 -0
  294. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-66493ba5a8367f2928812f446f47b56a.woff2 +0 -0
  295. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-670ba603758d94268e8606f240a42e12.woff2 +0 -0
  296. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-671a2c20b1eb9e4ef8a192833940e319.woff2 +0 -0
  297. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-684d65f1793cac449dde5d59cb3c47fb.woff2 +0 -0
  298. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-69c09cc5fa3e55c74fc4821f76909cc3.woff2 +0 -0
  299. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-6ae5b42180ad70b971c91e7eefb8eba2.woff2 +0 -0
  300. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-6e092f71c1e634059ada0e52abadce67.woff2 +0 -0
  301. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-6f3256af8454371776bc46670d33cc65.woff2 +0 -0
  302. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-6fe5c5973cc06f74b2387a631ea36b88.woff2 +0 -0
  303. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-70c2eb8d64e71a42a834eb857ea9df51.woff2 +0 -0
  304. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-70e811fd7994e61f408c923de6ddd078.woff2 +0 -0
  305. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-7197d6fda6cba7c3874c53d6381ca239.woff2 +0 -0
  306. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-72252d73220fa3cd856677888cee1635.woff2 +0 -0
  307. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-72536a3d71b694a0d53dd90ddceae41e.woff2 +0 -0
  308. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-726303e0774b4e678bff8c2deb6ca603.woff2 +0 -0
  309. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-733171b4ffcd17ea1fe1c0ba627173bf.woff2 +0 -0
  310. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-739bc1a567439c7cffcd1614644593d2.woff2 +0 -0
  311. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-73e309718fd16cea44b4d54a33581811.woff2 +0 -0
  312. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-7494dc504ae00ee9cd0505f990f88c5d.woff2 +0 -0
  313. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-74e2263a91439c25b91d5132ce9f4d62.woff2 +0 -0
  314. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-761d05e3cd968cf574166867998ef06a.woff2 +0 -0
  315. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-7718fe60986d8b42b1be9c5ace5ccf25.woff2 +0 -0
  316. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-774d4f764a1299da5d28ec2f2ffe0d69.woff2 +0 -0
  317. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-79d494361ae093b69e74ee9dbe65bfd4.woff2 +0 -0
  318. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-79f007c1c6d07557120982951ea67998.woff2 +0 -0
  319. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-7a07ddc0f0c0f5f4a9bad6ee3dda66b5.woff2 +0 -0
  320. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-7ab2bed91166a9dca83a5ebfbe2a7f38.woff2 +0 -0
  321. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-7ccce86603f80a099ddb0cb21d4ae3e3.woff2 +0 -0
  322. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-7e4bde7e9c7f84cd34d8a845e384c746.woff2 +0 -0
  323. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-7e929f262f30c8ee78bf398150b1a7cd.woff2 +0 -0
  324. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-7eb9fffd1aa890d07d0f88cc82e6cfe4.woff2 +0 -0
  325. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-7f855356ab893b0d2b9c1c83b8116f0e.woff2 +0 -0
  326. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-829615148e6357d826b9242eb7fbbd1e.woff2 +0 -0
  327. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-866fa7613df6b3fd272bcfd4530c0bb9.woff2 +0 -0
  328. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-87599f94b6cc129d505b375798d0d751.woff2 +0 -0
  329. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-8c2f33cee3993174f7e87c28e4bf42ee.woff2 +0 -0
  330. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-8d3bcabb847b56243b16afe62adaaf21.woff2 +0 -0
  331. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-8de5b863cb50dfefdd07cb11c774d579.woff2 +0 -0
  332. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-8e9f97f01034820170065b2921b4fb5e.woff2 +0 -0
  333. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-8f476c4c99813d57cbe6eca4727388ad.woff2 +0 -0
  334. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-903bb6865f3452e2fda42e3a25547bc5.woff2 +0 -0
  335. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-91ddb2969bf2d31ba02ad82998d1314c.woff2 +0 -0
  336. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-938d90c10ff8c20386af7f242c05d6b0.woff2 +0 -0
  337. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-93fc8f28a33234bcadf1527cafabd502.woff2 +0 -0
  338. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-95429962233afd82db1c27df1500a28c.woff2 +0 -0
  339. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-9544732d2e62d1a429674f8ee41b5d3a.woff2 +0 -0
  340. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-9592bfc861f07bcb8d75c196b370e548.woff2 +0 -0
  341. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-95bfd249da4902577b4b7d76ebdd0b44.woff2 +0 -0
  342. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-968cffdc8ee679da094e77ebf50f58ef.woff2 +0 -0
  343. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-97f7f48ce90c9429bf32ae51469db74d.woff2 +0 -0
  344. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-982b630266d87db93d2539affb1275c6.woff2 +0 -0
  345. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-98f2ad84457de7f3740d9920b8fa8667.woff2 +0 -0
  346. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-99a16ef6a64934d5781933dbd9c46b2e.woff2 +0 -0
  347. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-9cfb2a77a4e45025105ad29a1748b90d.woff2 +0 -0
  348. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-9d81066dd2b337c938df6e90380a00dc.woff2 +0 -0
  349. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-9eb5a99df4e76ac3363453ac9ca288b1.woff2 +0 -0
  350. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-a004ddfcb26e67bd6e678c8ed19e25ce.woff2 +0 -0
  351. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-a0ca5df4258213d7fc9fce80f65ce760.woff2 +0 -0
  352. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-a1f916d6039285c4ffb900cd654e418f.woff2 +0 -0
  353. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-a203b91dad570bf05a58c3c3ddb529bf.woff2 +0 -0
  354. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-a365e82ed54697a52f27adcea1315fe8.woff2 +0 -0
  355. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-a4c34be6d42152e64b0df90bc4607f64.woff2 +0 -0
  356. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-a7accba310e821da5505f71c03b76bdb.woff2 +0 -0
  357. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-aa0d470430e6391eca720c7cfa44446f.woff2 +0 -0
  358. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-aa5c9ca6cf4fba00433b7aa3fa10671a.woff2 +0 -0
  359. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-ac9ceb44437becc3e9c4dbfebab7fc2d.woff2 +0 -0
  360. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-b1220a3c61f85cc0408deedb4c5f57a2.woff2 +0 -0
  361. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-b358f7a51ece39a3247942b1feabdb29.woff2 +0 -0
  362. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-b57aaedfd8ebdf3931f25119dc6a5eb2.woff2 +0 -0
  363. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-b5c1596551c256e0e9cf02028595b092.woff2 +0 -0
  364. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-b6d128682ee29e471486354d486a1b90.woff2 +0 -0
  365. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-b6fd38ca30869792244804b04bc058da.woff2 +0 -0
  366. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-b7d203b051eff504ff59ddca7576b6a9.woff2 +0 -0
  367. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-b96d9226ce77ec94ceca043d712182e6.woff2 +0 -0
  368. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-ba3de316d63c7e339987b16f41a0b879.woff2 +0 -0
  369. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-bafff7a14c27403dcc6cf1432e8ea836.woff2 +0 -0
  370. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-bd77e3c7f9e0b072d96af37f73d1aa32.woff2 +0 -0
  371. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-be549ab72f0719d606a5c01e2c0219b6.woff2 +0 -0
  372. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-c16ed9740b85badf16e86ea782a3062f.woff2 +0 -0
  373. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-c1f94158256bb1f3bf665b053d895af9.woff2 +0 -0
  374. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-c40533fdf4cc57177b12803598af7e59.woff2 +0 -0
  375. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-c4a687ac4f0c2766eefc9f77ed99cddf.woff2 +0 -0
  376. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-c69f61a4ab18d0488c8d1fc12e7028e8.woff2 +0 -0
  377. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-c8b71798409ccc126ee264a00aadcf21.woff2 +0 -0
  378. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-c99eda15fc26a2941579560f76c3a5cf.woff2 +0 -0
  379. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-cb17fc3db95f6d139afc9d31a8e93293.woff2 +0 -0
  380. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-cbaaefaaf326668277aa24dfa93c4d28.woff2 +0 -0
  381. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-cd145ce4a0ea18469358df53c207bc1b.woff2 +0 -0
  382. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-cdbce89e82cc1ab53a2decbf5819278f.woff2 +0 -0
  383. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-ce4884f96f11589608b76b726a755803.woff2 +0 -0
  384. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-cf2cc71752631e579e35b0e423bf2638.woff2 +0 -0
  385. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-cf6ff4e0f491ca0cf3038187a997b9b4.woff2 +0 -0
  386. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-cfb211578629b7e8153b37240de6a9d5.woff2 +0 -0
  387. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-d0cf73942fea1c74edbdf0b3011f4656.woff2 +0 -0
  388. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-d2666cbed13462c5dc36fa2f15c202ca.woff2 +0 -0
  389. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-d3716376641d615e2995605b29bca7b6.woff2 +0 -0
  390. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-dac48066b5883d8b4551fc584f0c2a3e.woff2 +0 -0
  391. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-dbea1af6dcd9860be40c3d18254338f5.woff2 +0 -0
  392. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-e11567fd2accf9957cd0d3c2be937d87.woff2 +0 -0
  393. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-e2ead7ea7da0437f085f42ffc05f8d13.woff2 +0 -0
  394. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-e3fcf5180fd466c8915c4e8069491054.woff2 +0 -0
  395. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-e480d9c614742d05f0e78f274f1e69e6.woff2 +0 -0
  396. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-e4bca6cfa53e499cae0a6be4894a90e9.woff2 +0 -0
  397. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-e51ef413167c6e14e0c0fdcc585f2fc9.woff2 +0 -0
  398. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-e5f453bb04da18eed01675eeebd88bf8.woff2 +0 -0
  399. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-e656f091b9dc4709722c9f4b84d3c797.woff2 +0 -0
  400. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-ec181b795ac1fb5a50d700b6e996d745.woff2 +0 -0
  401. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-ee8bae97908d5147b423f77ad0d3c1bb.woff2 +0 -0
  402. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-f0f13b5c60e0af5553bd359f5513be1b.woff2 +0 -0
  403. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-f2b54d4e7be0eaefe1c2c56836fa5368.woff2 +0 -0
  404. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-f56414bf9bced67990def8660e306759.woff2 +0 -0
  405. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-f5d079153c99a25b9be5b8583c4cc8a7.woff2 +0 -0
  406. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-f6032fc06eb20480f096199713f70885.woff2 +0 -0
  407. package/skills/excalidraw/app/dist/fonts/Xiaolai/Xiaolai-Regular-f8ee5d36068a42b51d0e4a1116cfcec1.woff2 +0 -0
  408. package/skills/excalidraw/app/dist/index.html +16 -0
  409. package/skills/excalidraw/app/index.html +14 -0
  410. package/skills/excalidraw/app/package.json +20 -0
  411. package/skills/excalidraw/app/pnpm-lock.yaml +2593 -0
  412. package/skills/excalidraw/app/src/main.jsx +334 -0
  413. package/skills/excalidraw/app/src/scene.js +247 -0
  414. package/skills/excalidraw/app/vite.config.js +19 -0
  415. package/skills/excalidraw/assets/examples/flowchart.excalidraw +19 -0
  416. package/skills/excalidraw/references/setup.md +27 -0
  417. package/skills/excalidraw/references/skeleton.md +84 -0
  418. package/skills/excalidraw/references/style.md +46 -0
  419. package/skills/excalidraw/references/sync-protocol.md +47 -0
  420. package/skills/excalidraw/scripts/excal.mjs +416 -0
  421. package/skills/excalidraw/scripts/lib.mjs +81 -0
  422. package/skills/excalidraw/scripts/server.mjs +476 -0
@@ -0,0 +1,84 @@
1
+ # 스켈레톤 작성 규칙
2
+
3
+ `version` 필드가 없는 요소는 스켈레톤으로 취급한다. 창이 열리면 앱이 공식 `convertToExcalidrawElements`(id 유지)로 정식 요소로 바꾸고 파일을 다시 저장한다. 그 뒤 파일에는 `version`, `seed`, `boundElements` 같은 필드가 붙는다. 스켈레톤과 정식 요소는 한 파일에 섞여 있어도 된다.
4
+
5
+ 공식 문서: <https://docs.excalidraw.com/docs/@excalidraw/excalidraw/api/excalidraw-element-skeleton>
6
+
7
+ ## 파일 뼈대
8
+
9
+ ```json
10
+ {
11
+ "type": "excalidraw",
12
+ "version": 2,
13
+ "source": "pi-excalidraw",
14
+ "elements": [],
15
+ "appState": { "viewBackgroundColor": "#ffffff", "gridSize": 20 },
16
+ "files": {}
17
+ }
18
+ ```
19
+
20
+ ## 도형과 라벨
21
+
22
+ `rectangle` / `ellipse` / `diamond`. 모든 도형에 의미 있는 `id`와 `x`, `y`, `width`, `height`를 준다. 화살표 자동 연결과 lint가 이 값에 의존한다.
23
+
24
+ ```json
25
+ { "type": "rectangle", "id": "api", "x": 100, "y": 100, "width": 200, "height": 72,
26
+ "backgroundColor": "#a5d8ff", "fillStyle": "solid", "roundness": { "type": 3 },
27
+ "label": { "text": "API 서버" } }
28
+ ```
29
+
30
+ - `label`: `text`(필수), `fontSize`(기본 20), `strokeColor`, `textAlign`(`left|center|right`), `verticalAlign`(`top|middle|bottom`). 줄바꿈은 `\n`.
31
+ - `roundness: { "type": 3 }`: 모서리가 둥근 사각형. 생략하면 직각이다.
32
+
33
+ ## 텍스트
34
+
35
+ ```json
36
+ { "type": "text", "id": "title", "x": 100, "y": 20, "text": "결제 흐름", "fontSize": 28 }
37
+ ```
38
+
39
+ 크기는 앱이 측정하니 `width`/`height`는 생략한다.
40
+
41
+ ## 화살표와 선
42
+
43
+ 가장 쉬운 형태는 좌표 없이 연결할 두 요소의 id만 적는 것이다. 앱이 두 도형의 중심을 잇고 가장자리에서 끊어 좌표를 계산한 뒤 바인딩한다.
44
+
45
+ ```json
46
+ { "type": "arrow", "id": "api-db", "start": { "id": "api" }, "end": { "id": "db" }, "label": { "text": "SQL" } }
47
+ ```
48
+
49
+ 직접 경로를 지정하려면 `x`, `y`와 상대 좌표 `points`를 준다. 꺾인 선은 점을 3개 이상 둔다.
50
+
51
+ ```json
52
+ { "type": "arrow", "id": "loop", "x": 300, "y": 136, "points": [[0,0],[60,0],[60,-120],[-100,-120]],
53
+ "start": { "id": "b" }, "end": { "id": "a" } }
54
+ ```
55
+
56
+ - `startArrowhead` / `endArrowhead`: `null | "arrow" | "triangle" | "bar" | "circle" | "diamond"`, ERD용 `"crowfoot_one" | "crowfoot_many" | "crowfoot_one_or_many"`. 화살표 기본값은 끝에만 `arrow`.
57
+ - 연결 없는 선: `{ "type": "line", "x": 0, "y": 400, "points": [[0,0],[800,0]], "strokeStyle": "dashed" }`
58
+ - 양방향: `"startArrowhead": "arrow"`
59
+
60
+ ## 프레임(묶음 박스)
61
+
62
+ ```json
63
+ { "type": "frame", "id": "vpc", "name": "VPC", "children": ["api", "db"] }
64
+ ```
65
+
66
+ frame 크기는 자식과 자식에 연결된 화살표까지 감싸도록 자동 계산된다(`x`/`y`/`width`는 무시). 그래서 frame 밖으로 이어지는 화살표가 있으면 frame이 그쪽으로 늘어난다. 그런 경우와 단순히 영역만 보여주려면 프레임 대신 큰 `rectangle`(배경 투명, `strokeStyle: "dashed"`)을 먼저 배치하고 안에 도형을 둔다. 완전히 감싸는 도형끼리의 포함 관계는 lint가 겹침으로 보지 않는다.
67
+
68
+ ## 공통 스타일 속성
69
+
70
+ | 속성 | 값 |
71
+ |---|---|
72
+ | `strokeColor` / `backgroundColor` | hex 또는 `"transparent"` |
73
+ | `fillStyle` | `hachure`(기본, 빗금) · `cross-hatch` · `solid` · `zigzag` |
74
+ | `strokeWidth` | `1` · `2`(기본) · `4` |
75
+ | `strokeStyle` | `solid` · `dashed` · `dotted` |
76
+ | `roughness` | `0` 반듯함 · `1` 손그림(기본) · `2` 더 거칠게 |
77
+ | `opacity` | 0–100 |
78
+ | `fontFamily` | `5` Excalifont(기본, 손글씨, 한글 손글씨 폰트 지원) · `6` Nunito · `8` Comic Shanns · `3` Cascadia(코드) |
79
+
80
+ ## 정식 요소를 고칠 때
81
+
82
+ - 이동: 도형의 `x`/`y`만 바꾼다. 안의 라벨과 연결된 직선 화살표는 앱이 다시 맞춘다.
83
+ - 라벨 텍스트: 도형을 스켈레톤으로 다시 쓰는 것이 가장 안전하다. `version`, `versionNonce`, `seed`, `boundElements`를 빼고 `id`, 좌표, 스타일, `label`만 남긴다. 기존 라벨 텍스트 요소는 앱이 지운다.
84
+ - 화살표 경로 초기화: 화살표를 `{ "type": "arrow", "id": "<같은 id>", "start": {"id": …}, "end": {"id": …} }`로 다시 쓴다. 라벨이 있었다면 `label`도 다시 넣는다.
@@ -0,0 +1,46 @@
1
+ # 스타일과 레이아웃
2
+
3
+ ## 팔레트 (Excalidraw 기본 색)
4
+
5
+ | 용도 | 배경 `backgroundColor` | 선 `strokeColor` |
6
+ |---|---|---|
7
+ | 기본 처리·서비스 | `#a5d8ff` | `#1971c2` |
8
+ | 시작·성공·완료 | `#b2f2bb` | `#2f9e44` |
9
+ | 판단·주의 | `#ffec99` | `#f08c00` |
10
+ | 오류·실패·외부 위험 | `#ffc9c9` | `#e03131` |
11
+ | 저장소·DB | `#d0bfff` | `#6741d9` |
12
+ | 외부 시스템·사용자 | `#e9ecef` | `#495057` |
13
+ | 강조 메모 | `#fff3bf` | `#e67700` |
14
+
15
+ - 한 그림에 의미 색은 4개 이하로 쓴다. 나머지는 흰 배경에 기본 선(`#1e1e1e`).
16
+ - 채움은 `fillStyle: "solid"`가 읽기 편하다. 손그림 느낌을 강하게 원하면 기본 `hachure`로 둔다.
17
+ - 글꼴은 기본(Excalifont)을 유지한다. 한글도 손글씨 폰트로 나온다. 다른 폰트는 한글이 시스템 폰트로 대체된다.
18
+
19
+ ## 크기와 간격 (20px 그리드 기준)
20
+
21
+ - 일반 노드 `200×72`, 긴 라벨 노드 `240×80`, 판단 다이아몬드 `220×120`, 시작/끝 타원 `160×64`.
22
+ - 라벨 폰트 20, 제목 28–36, 보조 설명 16.
23
+ - 라벨 폭 추정: 한글 한 글자 ≈ fontSize, 영문 ≈ 0.55×fontSize. 좌우 여백 24px를 더한 값이 `width`보다 크면 너비를 늘리거나 `\n`으로 줄을 나눈다.
24
+ - 노드 사이 간격: 라벨 없는 화살표 60px 이상, 라벨 있는 화살표 120px 이상(라벨이 화살표 위에 올라간다).
25
+ - 좌표는 20의 배수로 맞춘다. 같은 열의 노드는 `x`를, 같은 행은 `y`를 똑같이 둔다.
26
+
27
+ ## 레시피
28
+
29
+ **세로 플로우차트**: 한 열에 `x` 고정, 노드 높이 72 + 간격 60 → 행 간격 140(라벨 화살표가 있으면 180). 분기는 다이아몬드 오른쪽으로 320px 떨어진 열에 둔다. 제목은 맨 위 `y=20`.
30
+
31
+ **가로 파이프라인**: 한 행에 `y` 고정, 노드 폭 200 + 간격 100 → 열 간격 300. 5단계를 넘으면 두 줄로 접고 줄 끝에서 꺾인 화살표로 잇는다.
32
+
33
+ **아키텍처(계층)**: 위에서 아래로 클라이언트 → 엣지/게이트웨이 → 서비스 → 데이터 계층. 계층마다 점선 `rectangle`(배경 투명, 선 `#868e96`)으로 감싸고 왼쪽 위에 16px 텍스트로 계층 이름을 둔다. 계층 간 세로 간격 80 이상.
34
+
35
+ **시퀀스**: 참여자를 가로로 240px 간격에 두고, 각 아래로 점선 `line`(생명선)을 그린다. 메시지는 `y`를 50px씩 내리며 수평 `arrow`에 `label`을 단다. 응답은 `strokeStyle: "dashed"`. 참여자가 5개를 넘거나 메시지가 15개를 넘으면 Mermaid `sequenceDiagram`으로 가져온다.
36
+
37
+ **마인드맵**: 중심 타원을 가운데 두고 1차 가지는 반지름 300 원 위에, 2차는 바깥 180px에 둔다. 가지별로 색을 하나씩 준다.
38
+
39
+ **와이어프레임**: `roughness: 0`이 아니라 기본값 1을 유지해 스케치임을 드러낸다. 화면 프레임 `rectangle` 안에 영역 박스를 배치하고, 버튼은 `roundness: {type: 3}` 작은 사각형, 텍스트 자리는 회색 `line`으로 표현한다.
40
+
41
+ ## 셀프 체크 (snapshot 볼 때)
42
+
43
+ - 라벨이 도형 밖으로 넘치거나 두 줄로 어색하게 접히지 않았는가
44
+ - 화살표 라벨이 노드와 겹치지 않는가
45
+ - 화살표가 다른 노드를 관통하지 않는가 (관통하면 노드를 옮기거나 `points`로 우회)
46
+ - 색이 의미대로 쓰였는가, 제목이 있는가
@@ -0,0 +1,47 @@
1
+ # 앱 내부 구조 (유지보수용)
2
+
3
+ ## 구성
4
+
5
+ - `scripts/excal.mjs`: CLI. 빌드 확인, 데몬 기동, 파일 등록, Chrome `--app` 창 실행, lint/inspect.
6
+ - `scripts/server.mjs`: Node 내장 모듈만 쓰는 데몬. `127.0.0.1`에만 바인딩. 기본 포트 47813(사용 중이면 임의 포트). 창 푸시는 최소 WebSocket 구현(서버→창 텍스트 프레임, 20초 ping). SSE는 Chrome의 origin당 HTTP/1.1 연결 6개를 창마다 하나씩 차지해 6번째 창부터 모든 요청이 막히므로 쓰지 않는다.
7
+ - `app/`: Vite + React + `@excalidraw/excalidraw` 0.18.1 + `@excalidraw/mermaid-to-excalidraw`. `app/src/scene.js`에 변환·병합 순수 로직, `app/src/main.jsx`에 동기화 로직.
8
+ - 상태: `~/.cache/pi-excalidraw/` (`EXCAL_STATE_DIR`로 변경). `server.json`(pid/port), `token`, `files.json`(id→경로), `history/<id>/`, `snapshots/`, `server.log`, `chrome-profile/`.
9
+
10
+ ## 환경 변수
11
+
12
+ | 변수 | 용도 |
13
+ |---|---|
14
+ | `EXCAL_STATE_DIR` | 상태 디렉터리 |
15
+ | `EXCAL_PORT` | 선호 포트 |
16
+ | `EXCAL_IDLE_MINUTES` | 무접속 자동 종료(기본 30) |
17
+ | `EXCAL_BROWSER=none` | 창을 띄우지 않음 |
18
+ | `EXCAL_CHROME_ARGS` | Chrome 추가 인자 (예: `--remote-debugging-port=9333`) |
19
+
20
+ ## 동기화
21
+
22
+ - 파일 id는 실제 경로의 sha1 앞 12자리. 창 URL은 `/?file=<id>&token=<token>`.
23
+ - 창은 `ws://…/api/files/<id>/ws?clientId&token`에 붙는다. 메시지는 `{event, data}` JSON이고 event는 `hello`·`scene`·`snapshot`·`problem`. 끊기면 지수 백오프(최대 5초)로 재접속하고 `hello`를 받으면 최신 scene을 GET한다.
24
+ - 서버는 파일이 아니라 디렉터리를 `fs.watch`한다(임시 파일 + rename 저장 대응). 80ms debounce 후 해시가 바뀌었고 JSON이 유효하면 `rev`를 올리고 `scene` 이벤트로 내용을 보낸다. 받아들이기 직전 내용은 `history/`에 백업한다.
25
+ - 창은 PUT `/api/files/<id>/scene`에 `{baseRev, content, clientId}`를 보낸다. `baseRev`가 현재 `rev`와 다르면 409와 최신 내용을 돌려준다. 서버는 자기가 쓴 해시를 기억해 watch 에코를 무시한다.
26
+ - 창의 원격 반영 순서:
27
+ 1. `pendingMermaid`가 있으면 mermaid 결과로 요소 전체 교체.
28
+ 2. 스켈레톤 정규화(`convertMixed`). 정식 frame에는 `children: []`를 넣어 변환기 크래시를 막는다.
29
+ 3. `repairScene`: 도형 이동에 따른 직선 화살표 재계산, 바인딩 역참조 보강.
30
+ 4. `restoreElements(remote, local, {refreshDimensions, repairBindings})`: 텍스트 재측정, 끊긴 바인딩 제거.
31
+ 5. `recenterLabels`: 재측정된 크기로 라벨을 도형 안에 다시 정렬.
32
+ 6. 저장 안 된 로컬 변경이 있으면 `threeWayMerge`.
33
+ 7. 파일에서 사라진 요소는 `isDeleted` tombstone으로, 내용이 바뀐 요소는 `version+1`·새 `versionNonce`로 만든다. Excalidraw Store는 versionNonce로 변경을 감지하므로 이게 없으면 LLM 변경이 히스토리에 안 남고, 이후 Cmd+Z가 LLM 이전 값으로 되감긴다.
34
+ 8. `updateScene({captureUpdate: IMMEDIATELY})`, 바뀐 요소는 토스트로만 알린다(선택 상태는 건드리지 않음).
35
+ 9. 1–5단계에서 내용이 바뀌었거나 병합으로 로컬 변경이 남았으면 곧바로 파일에 다시 저장한다.
36
+ - 3-way 기준선: 마지막 동기화 시점의 요소별 `{version, contentKey}`. 로컬만 바뀐 요소는 로컬, 원격이 바뀐 요소는 원격이 이긴다.
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는 실패로 끝난다.
39
+
40
+ ## 보안
41
+
42
+ - `127.0.0.1` 바인딩, Host 헤더 검사(`127.0.0.1:<port>`, `localhost:<port>`만 허용, WebSocket upgrade 포함), `/api/*`는 토큰(`x-excal-token` 헤더 또는 `token` 쿼리) 필수.
43
+ - 읽기·쓰기는 `excal open`으로 등록한 파일만 가능하다.
44
+
45
+ ## 폰트
46
+
47
+ 빌드할 때 `node_modules/@excalidraw/excalidraw/dist/prod/fonts`를 `dist/fonts`로 복사하고 `window.EXCALIDRAW_ASSET_PATH = "/"`로 로컬에서 서빙한다(오프라인 동작, 한글은 Xiaolai 손글씨 대체 폰트). 로컬 로드에 실패하면 Excalidraw가 CDN으로 폴백한다.
@@ -0,0 +1,416 @@
1
+ #!/usr/bin/env node
2
+ // excal: open .excalidraw files in a live-synced local Excalidraw window,
3
+ // inspect/lint them, and take PNG snapshots for visual self-checks.
4
+ import { spawn, spawnSync } from "node:child_process";
5
+ import fs from "node:fs";
6
+ import path from "node:path";
7
+ import {
8
+ APP_DIR,
9
+ CHROME_PROFILE_DIR,
10
+ DIST_DIR,
11
+ emptyScene,
12
+ ensureStateDir,
13
+ fileIdFor,
14
+ HISTORY_DIR,
15
+ LOG_FILE,
16
+ readJSON,
17
+ readToken,
18
+ realPathOf,
19
+ SKILL_DIR,
20
+ SNAPSHOT_DIR,
21
+ STATE_FILE,
22
+ writeFileAtomic,
23
+ } from "./lib.mjs";
24
+
25
+ const USAGE = `usage:
26
+ excal open <file.excalidraw> [--from-mermaid <file.mmd>] [--no-window]
27
+ excal inspect <file.excalidraw>
28
+ excal lint <file.excalidraw>
29
+ excal snapshot <file.excalidraw> [-o out.png]
30
+ excal history <file.excalidraw>
31
+ excal status | stop | build`;
32
+
33
+ const die = (msg, code = 1) => {
34
+ console.error(msg);
35
+ process.exit(code);
36
+ };
37
+
38
+ // ---------- build / server ----------
39
+
40
+ function ensureBuilt(force = false) {
41
+ if (!force && fs.existsSync(path.join(DIST_DIR, "index.html"))) return;
42
+ const pm = spawnSync("pnpm", ["--version"]).status === 0 ? "pnpm" : "npm";
43
+ if (!fs.existsSync(path.join(APP_DIR, "node_modules"))) {
44
+ console.error(`[excal] 첫 실행: ${pm} install (1회만)`);
45
+ const r = spawnSync(pm, ["install"], { cwd: APP_DIR, stdio: ["ignore", 2, 2] });
46
+ if (r.status !== 0) die("[excal] install 실패");
47
+ }
48
+ console.error("[excal] 앱 빌드 중");
49
+ const r = spawnSync(pm, ["run", "build"], { cwd: APP_DIR, stdio: ["ignore", 2, 2] });
50
+ if (r.status !== 0) die("[excal] build 실패");
51
+ }
52
+
53
+ async function call(port, token, pathname, init = {}) {
54
+ return fetch(`http://127.0.0.1:${port}${pathname}`, {
55
+ ...init,
56
+ headers: { "x-excal-token": token, "content-type": "application/json", ...(init.headers || {}) },
57
+ });
58
+ }
59
+
60
+ async function healthy(state, token) {
61
+ if (!state?.port) return false;
62
+ try {
63
+ const r = await call(state.port, token, "/api/health", { signal: AbortSignal.timeout(1000) });
64
+ return r.ok;
65
+ } catch {
66
+ return false;
67
+ }
68
+ }
69
+
70
+ async function ensureServer() {
71
+ ensureStateDir();
72
+ ensureBuilt();
73
+ const token = readToken();
74
+ let state = readJSON(STATE_FILE, null);
75
+ if (await healthy(state, token)) return { port: state.port, token };
76
+ const log = fs.openSync(LOG_FILE, "a");
77
+ spawn(process.execPath, [path.join(SKILL_DIR, "scripts", "server.mjs")], {
78
+ detached: true,
79
+ stdio: ["ignore", log, log],
80
+ }).unref();
81
+ for (let i = 0; i < 50; i++) {
82
+ await new Promise((r) => setTimeout(r, 200));
83
+ state = readJSON(STATE_FILE, null);
84
+ if (await healthy(state, token)) return { port: state.port, token };
85
+ }
86
+ die(`[excal] 서버 시작 실패. 로그: ${LOG_FILE}`);
87
+ }
88
+
89
+ async function register(srv, file) {
90
+ const r = await call(srv.port, srv.token, "/api/files", { method: "POST", body: JSON.stringify({ path: file }) });
91
+ if (!r.ok) die(`[excal] 파일 등록 실패: ${await r.text()}`);
92
+ return r.json();
93
+ }
94
+
95
+ function chromeCommand(url) {
96
+ const args = [
97
+ `--app=${url}`,
98
+ `--user-data-dir=${CHROME_PROFILE_DIR}`,
99
+ "--no-first-run",
100
+ "--no-default-browser-check",
101
+ "--window-size=1440,960",
102
+ ...(process.env.EXCAL_CHROME_ARGS ? process.env.EXCAL_CHROME_ARGS.split(/\s+/).filter(Boolean) : []),
103
+ ];
104
+ if (process.platform === "darwin") return ["open", ["-na", "Google Chrome", "--args", ...args]];
105
+ for (const bin of ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser"]) {
106
+ if (spawnSync("which", [bin]).status === 0) return [bin, args];
107
+ }
108
+ return null;
109
+ }
110
+
111
+ async function openWindow(srv, info) {
112
+ if (process.env.EXCAL_BROWSER === "none") return false;
113
+ const cmd = chromeCommand(info.url);
114
+ if (!cmd) {
115
+ console.error(`[excal] Chrome을 찾지 못했습니다. 직접 여세요: ${info.url}`);
116
+ return false;
117
+ }
118
+ spawn(cmd[0], cmd[1], { detached: true, stdio: "ignore" }).unref();
119
+ return waitForClient(srv, info.id, 20_000);
120
+ }
121
+
122
+ async function waitForClient(srv, id, timeoutMs) {
123
+ const deadline = Date.now() + timeoutMs;
124
+ while (Date.now() < deadline) {
125
+ const st = await (await call(srv.port, srv.token, "/api/status")).json();
126
+ if (st.files.find((f) => f.id === id)?.clients > 0) return true;
127
+ await new Promise((r) => setTimeout(r, 250));
128
+ }
129
+ return false;
130
+ }
131
+
132
+ function resolveFile(p, { create = false } = {}) {
133
+ if (!p) die(USAGE);
134
+ const abs = path.resolve(p);
135
+ if (!fs.existsSync(abs)) {
136
+ if (!create) die(`[excal] 파일이 없습니다: ${abs}`);
137
+ fs.mkdirSync(path.dirname(abs), { recursive: true });
138
+ fs.writeFileSync(abs, emptyScene());
139
+ }
140
+ return realPathOf(abs);
141
+ }
142
+
143
+ // ---------- scene analysis (inspect / lint) ----------
144
+
145
+ function loadScene(file) {
146
+ let data;
147
+ try {
148
+ data = JSON.parse(fs.readFileSync(file, "utf8"));
149
+ } catch (err) {
150
+ return { error: `JSON 파싱 실패: ${err.message}` };
151
+ }
152
+ const elements = (Array.isArray(data.elements) ? data.elements : []).filter((e) => !e.isDeleted);
153
+ return { data, elements };
154
+ }
155
+
156
+ const isSkel = (e) => typeof e.version !== "number";
157
+ const SHAPES = new Set(["rectangle", "ellipse", "diamond"]);
158
+ const LINEAR = new Set(["arrow", "line"]);
159
+ const round = (n) => (typeof n === "number" ? Math.round(n) : "?");
160
+
161
+ function textWidthEstimate(text, fontSize = 20) {
162
+ let max = 0;
163
+ for (const line of String(text).split("\n")) {
164
+ let w = 0;
165
+ for (const ch of line)
166
+ w += /[\u1100-\u11ff\u3000-\u9fff\uac00-\ud7af\uff00-\uffef]/.test(ch) ? fontSize : fontSize * 0.55;
167
+ max = Math.max(max, w);
168
+ }
169
+ return max;
170
+ }
171
+
172
+ function labelOf(el, byId) {
173
+ if (el.label?.text) return el.label.text;
174
+ if (el.type === "text") return el.text;
175
+ const bound = (el.boundElements || []).find((b) => b.type === "text");
176
+ return bound ? byId.get(bound.id)?.text : undefined;
177
+ }
178
+
179
+ function arrowEnds(el) {
180
+ const s = el.start?.id ?? el.startBinding?.elementId;
181
+ const e = el.end?.id ?? el.endBinding?.elementId;
182
+ return [s, e];
183
+ }
184
+
185
+ function inspect(file) {
186
+ const { error, elements } = loadScene(file);
187
+ if (error) die(error);
188
+ const byId = new Map(elements.map((e) => [e.id, e]));
189
+ const visible = elements.filter((e) => !(e.type === "text" && e.containerId));
190
+ 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)));
196
+ console.log(
197
+ `${path.basename(file)} 요소 ${visible.length}개 (도형 ${count((e) => SHAPES.has(e.type))}, 화살표/선 ${count((e) => LINEAR.has(e.type))}, 텍스트 ${count((e) => e.type === "text")})` +
198
+ (boxes.length ? ` 범위 x ${round(minX)}~${round(maxX)}, y ${round(minY)}~${round(maxY)}` : "") +
199
+ (elements.some(isSkel) ? " [스켈레톤 포함: 창에서 열면 정규화됨]" : ""),
200
+ );
201
+ for (const el of visible) {
202
+ const label = labelOf(el, byId);
203
+ const lbl = label ? ` "${label.replace(/\n/g, "\\n")}"` : "";
204
+ const style = [
205
+ el.backgroundColor && el.backgroundColor !== "transparent" ? `bg=${el.backgroundColor}` : "",
206
+ el.strokeColor && el.strokeColor !== "#1e1e1e" ? `stroke=${el.strokeColor}` : "",
207
+ ]
208
+ .filter(Boolean)
209
+ .join(" ");
210
+ if (LINEAR.has(el.type)) {
211
+ const [s, e] = arrowEnds(el);
212
+ console.log(
213
+ `[${el.type}] ${el.id} ${s ?? "·"} → ${e ?? "·"}${lbl} @(${round(el.x)},${round(el.y)})${style ? " " + style : ""}`,
214
+ );
215
+ } else {
216
+ console.log(
217
+ `[${el.type}] ${el.id} @(${round(el.x)},${round(el.y)} ${round(el.width)}×${round(el.height)})${lbl}${style ? " " + style : ""}${el.frameId ? ` frame=${el.frameId}` : ""}`,
218
+ );
219
+ }
220
+ }
221
+ }
222
+
223
+ function lint(file) {
224
+ const { error, data, elements } = loadScene(file);
225
+ const errors = [];
226
+ const warns = [];
227
+ if (error) errors.push(error);
228
+ else {
229
+ if (data.type !== "excalidraw") errors.push(`최상위 type이 "excalidraw"가 아닙니다: ${data.type}`);
230
+ if (!Array.isArray(data.elements)) errors.push("최상위 elements 배열이 없습니다");
231
+ const all = Array.isArray(data.elements) ? data.elements : [];
232
+ const ids = new Map();
233
+ for (const e of all) {
234
+ if (!e.type) errors.push(`type 없는 요소: ${JSON.stringify(e).slice(0, 80)}`);
235
+ if (!e.id) {
236
+ if (isSkel(e) && !LINEAR.has(e.type)) warns.push(`id 없는 ${e.type} 요소 (나중에 수정·연결하려면 id 권장)`);
237
+ continue;
238
+ }
239
+ if (ids.has(e.id)) errors.push(`중복 id: ${e.id}`);
240
+ ids.set(e.id, e);
241
+ }
242
+ const ref = (owner, id, what) => {
243
+ if (id && !ids.has(id)) errors.push(`${owner.id ?? owner.type}의 ${what}가 없는 요소를 가리킴: ${id}`);
244
+ };
245
+ for (const e of elements) {
246
+ ref(e, e.containerId, "containerId");
247
+ ref(e, e.frameId, "frameId");
248
+ for (const b of e.boundElements || []) ref(e, b.id, "boundElements");
249
+ ref(e, e.startBinding?.elementId, "startBinding");
250
+ ref(e, e.endBinding?.elementId, "endBinding");
251
+ ref(e, e.start?.id, "start.id");
252
+ ref(e, e.end?.id, "end.id");
253
+ if (isSkel(e) && LINEAR.has(e.type)) {
254
+ const [s, t] = arrowEnds(e);
255
+ if (typeof e.x !== "number" && !(s && t))
256
+ errors.push(`${e.id ?? "arrow"}: x/y도 없고 start.id·end.id도 없습니다`);
257
+ if (typeof e.x !== "number" && s && t) {
258
+ for (const id of [s, t]) {
259
+ const target = ids.get(id);
260
+ if (target && (typeof target.width !== "number" || typeof target.height !== "number"))
261
+ errors.push(`${e.id ?? "arrow"}: 자동 연결 대상 ${id}에 width/height가 없습니다`);
262
+ }
263
+ }
264
+ }
265
+ if (isSkel(e) && SHAPES.has(e.type) && e.label?.text && typeof e.width === "number") {
266
+ const need = textWidthEstimate(e.label.text, e.label.fontSize ?? 20) + 24;
267
+ if (need > e.width)
268
+ warns.push(
269
+ `${e.id}: 라벨 "${e.label.text.split("\n")[0]}"이 너비 ${e.width}보다 길어 보임 (약 ${Math.round(need)}px 필요)`,
270
+ );
271
+ }
272
+ }
273
+ // partial overlaps between top-level boxes (containment is treated as intentional grouping)
274
+ const boxes = elements
275
+ .filter((e) => (SHAPES.has(e.type) || (e.type === "text" && !e.containerId)) && typeof e.x === "number")
276
+ .map((e) => {
277
+ const w =
278
+ typeof e.width === "number"
279
+ ? e.width
280
+ : e.type === "text"
281
+ ? textWidthEstimate(e.text, e.fontSize ?? 20)
282
+ : undefined;
283
+ const h =
284
+ typeof e.height === "number"
285
+ ? e.height
286
+ : e.type === "text"
287
+ ? (e.fontSize ?? 20) * 1.25 * String(e.text).split("\n").length
288
+ : undefined;
289
+ return w === undefined || h === undefined
290
+ ? null
291
+ : { id: e.id ?? e.type, x1: e.x, y1: e.y, x2: e.x + w, y2: e.y + h };
292
+ })
293
+ .filter(Boolean);
294
+ const contains = (a, b) => a.x1 <= b.x1 && a.y1 <= b.y1 && a.x2 >= b.x2 && a.y2 >= b.y2;
295
+ for (let i = 0; i < boxes.length; i++)
296
+ for (let j = i + 1; j < boxes.length; j++) {
297
+ const a = boxes[i];
298
+ const b = boxes[j];
299
+ const ix = Math.min(a.x2, b.x2) - Math.max(a.x1, b.x1);
300
+ const iy = Math.min(a.y2, b.y2) - Math.max(a.y1, b.y1);
301
+ if (ix > 2 && iy > 2 && !contains(a, b) && !contains(b, a))
302
+ warns.push(`겹침: ${a.id} ↔ ${b.id} (${Math.round(ix)}×${Math.round(iy)})`);
303
+ }
304
+ for (const e of elements) {
305
+ if (e.type !== "arrow") continue;
306
+ const [s, t] = arrowEnds(e);
307
+ if (!s || !t) warns.push(`${e.id ?? "arrow"}: 한쪽 이상이 도형에 연결되지 않은 화살표`);
308
+ }
309
+ }
310
+ for (const m of errors) console.log(`ERROR ${m}`);
311
+ for (const m of warns) console.log(`WARN ${m}`);
312
+ console.log(errors.length || warns.length ? `오류 ${errors.length}, 경고 ${warns.length}` : "OK");
313
+ process.exit(errors.length ? 1 : 0);
314
+ }
315
+
316
+ // ---------- commands ----------
317
+
318
+ const [cmd, ...rest] = process.argv.slice(2);
319
+ const flag = (name) => {
320
+ const i = rest.indexOf(name);
321
+ if (i === -1) return undefined;
322
+ const v = rest[i + 1];
323
+ rest.splice(i, 2);
324
+ return v;
325
+ };
326
+ const bool = (name) => {
327
+ const i = rest.indexOf(name);
328
+ if (i === -1) return false;
329
+ rest.splice(i, 1);
330
+ return true;
331
+ };
332
+
333
+ switch (cmd) {
334
+ case "open": {
335
+ const mermaid = flag("--from-mermaid");
336
+ const noWindow = bool("--no-window");
337
+ const file = resolveFile(rest[0], { create: true });
338
+ if (mermaid) {
339
+ const src = fs.readFileSync(path.resolve(mermaid), "utf8");
340
+ const data = JSON.parse(fs.readFileSync(file, "utf8"));
341
+ data.pendingMermaid = src;
342
+ writeFileAtomic(file, JSON.stringify(data, null, 2) + "\n");
343
+ }
344
+ const srv = await ensureServer();
345
+ const info = await register(srv, file);
346
+ let windowState = "already-open";
347
+ if (info.clients === 0) {
348
+ windowState = noWindow ? "not-opened" : (await openWindow(srv, info)) ? "opened" : "not-connected";
349
+ }
350
+ console.log(JSON.stringify({ file, url: info.url, window: windowState }, null, 2));
351
+ if (mermaid && windowState !== "not-opened")
352
+ console.error("[excal] mermaid 변환은 창에서 수행됩니다. 변환 후 파일이 정식 포맷으로 다시 저장됩니다.");
353
+ break;
354
+ }
355
+ case "inspect":
356
+ inspect(resolveFile(rest[0]));
357
+ break;
358
+ case "lint":
359
+ lint(resolveFile(rest[0]));
360
+ break;
361
+ case "snapshot": {
362
+ const out = flag("-o");
363
+ const file = resolveFile(rest[0]);
364
+ const srv = await ensureServer();
365
+ const info = await register(srv, file);
366
+ if (info.clients === 0 && !(await openWindow(srv, info)))
367
+ die("[excal] 창에 연결하지 못해 스냅샷을 찍을 수 없습니다");
368
+ const r = await call(srv.port, srv.token, `/api/files/${info.id}/snapshot`, { method: "POST" });
369
+ if (!r.ok) die(`[excal] 스냅샷 실패: ${await r.text()}`);
370
+ const target = out
371
+ ? path.resolve(out)
372
+ : path.join(SNAPSHOT_DIR, `${path.basename(file, path.extname(file))}-${info.id}.png`);
373
+ fs.mkdirSync(path.dirname(target), { recursive: true });
374
+ fs.writeFileSync(target, Buffer.from(await r.arrayBuffer()));
375
+ console.log(target);
376
+ break;
377
+ }
378
+ case "history": {
379
+ const file = resolveFile(rest[0]);
380
+ const dir = path.join(HISTORY_DIR, fileIdFor(file));
381
+ const items = fs.existsSync(dir)
382
+ ? fs
383
+ .readdirSync(dir)
384
+ .filter((f) => f.endsWith(".excalidraw"))
385
+ .sort()
386
+ .reverse()
387
+ : [];
388
+ if (!items.length) console.log("히스토리 없음");
389
+ for (const f of items)
390
+ console.log(`${new Date(Number(path.basename(f, ".excalidraw"))).toLocaleString("ko-KR")} ${path.join(dir, f)}`);
391
+ break;
392
+ }
393
+ case "status": {
394
+ const token = readToken();
395
+ const state = readJSON(STATE_FILE, null);
396
+ if (!(await healthy(state, token))) {
397
+ console.log("서버 꺼짐");
398
+ break;
399
+ }
400
+ console.log(JSON.stringify(await (await call(state.port, token, "/api/status")).json(), null, 2));
401
+ break;
402
+ }
403
+ case "stop": {
404
+ const token = readToken();
405
+ const state = readJSON(STATE_FILE, null);
406
+ if (await healthy(state, token)) await call(state.port, token, "/api/shutdown", { method: "POST" });
407
+ console.log("stopped");
408
+ break;
409
+ }
410
+ case "build":
411
+ ensureBuilt(true);
412
+ console.log("built");
413
+ break;
414
+ default:
415
+ die(USAGE);
416
+ }
@@ -0,0 +1,81 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ export const SKILL_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
8
+ export const APP_DIR = path.join(SKILL_DIR, "app");
9
+ export const DIST_DIR = path.join(APP_DIR, "dist");
10
+ export const STATE_DIR = process.env.EXCAL_STATE_DIR || path.join(os.homedir(), ".cache", "pi-excalidraw");
11
+ export const STATE_FILE = path.join(STATE_DIR, "server.json");
12
+ export const TOKEN_FILE = path.join(STATE_DIR, "token");
13
+ export const REGISTRY_FILE = path.join(STATE_DIR, "files.json");
14
+ export const HISTORY_DIR = path.join(STATE_DIR, "history");
15
+ export const SNAPSHOT_DIR = path.join(STATE_DIR, "snapshots");
16
+ export const LOG_FILE = path.join(STATE_DIR, "server.log");
17
+ export const CHROME_PROFILE_DIR = path.join(STATE_DIR, "chrome-profile");
18
+
19
+ export function ensureStateDir() {
20
+ fs.mkdirSync(STATE_DIR, { recursive: true, mode: 0o700 });
21
+ }
22
+
23
+ export function readToken() {
24
+ ensureStateDir();
25
+ try {
26
+ const token = fs.readFileSync(TOKEN_FILE, "utf8").trim();
27
+ if (token) return token;
28
+ } catch {}
29
+ const token = crypto.randomBytes(24).toString("hex");
30
+ fs.writeFileSync(TOKEN_FILE, token, { mode: 0o600 });
31
+ return token;
32
+ }
33
+
34
+ export function readJSON(file, fallback) {
35
+ try {
36
+ return JSON.parse(fs.readFileSync(file, "utf8"));
37
+ } catch {
38
+ return fallback;
39
+ }
40
+ }
41
+
42
+ export function sha(text) {
43
+ return crypto.createHash("sha256").update(text).digest("hex");
44
+ }
45
+
46
+ /** Stable id for a diagram file, derived from its real absolute path. */
47
+ export function fileIdFor(absPath) {
48
+ return crypto.createHash("sha1").update(absPath).digest("hex").slice(0, 12);
49
+ }
50
+
51
+ export function realPathOf(p) {
52
+ const abs = path.resolve(p);
53
+ try {
54
+ return fs.realpathSync(abs);
55
+ } catch {
56
+ return abs;
57
+ }
58
+ }
59
+
60
+ export function emptyScene() {
61
+ return (
62
+ JSON.stringify(
63
+ {
64
+ type: "excalidraw",
65
+ version: 2,
66
+ source: "pi-excalidraw",
67
+ elements: [],
68
+ appState: { viewBackgroundColor: "#ffffff", gridSize: 20 },
69
+ files: {},
70
+ },
71
+ null,
72
+ 2,
73
+ ) + "\n"
74
+ );
75
+ }
76
+
77
+ export function writeFileAtomic(file, content) {
78
+ const tmp = path.join(path.dirname(file), `.${path.basename(file)}.${process.pid}.${Date.now()}.tmp`);
79
+ fs.writeFileSync(tmp, content);
80
+ fs.renameSync(tmp, file);
81
+ }