excalidrop 0.1.4 → 0.2.1
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.
- package/README.md +58 -131
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +488 -394
- package/dist/cli.js.map +1 -1
- package/dist/frontend/assets/{advancedFormat-tiBAHNGo.js → advancedFormat-CsxROTkH.js} +1 -1
- package/dist/frontend/assets/{ar-SA-G6X2FPQ2-DVxiWV5Z.js → ar-SA-G6X2FPQ2-Bu8X6mOi.js} +1 -1
- package/dist/frontend/assets/arc-B9VNLSzj.js +1 -0
- package/dist/frontend/assets/{az-AZ-76LH7QW2-Z_2BqDo-.js → az-AZ-76LH7QW2-DpvzY31_.js} +1 -1
- package/dist/frontend/assets/{bg-BG-XCXSNQG7-DrxGJZOG.js → bg-BG-XCXSNQG7-DCtZnLyo.js} +1 -1
- package/dist/frontend/assets/{blockDiagram-38ab4fdb-CDnkTFfP.js → blockDiagram-38ab4fdb-4cfDkUsw.js} +1 -1
- package/dist/frontend/assets/{blockDiagram-68f4deed-Bb0W6mxN.js → blockDiagram-68f4deed-CsgWL6uC.js} +1 -1
- package/dist/frontend/assets/{bn-BD-2XOGV67Q-BkGJ-w8k.js → bn-BD-2XOGV67Q-B-IdLNKT.js} +1 -1
- package/dist/frontend/assets/{c4Diagram-15b5d702-ByRV0tka.js → c4Diagram-15b5d702-7BAqo4qT.js} +1 -1
- package/dist/frontend/assets/{c4Diagram-3d4e48cf-QSVnzYYK.js → c4Diagram-3d4e48cf-CDW-O-vg.js} +1 -1
- package/dist/frontend/assets/{ca-ES-6MX7JW3Y-BBJvOPE1.js → ca-ES-6MX7JW3Y-DM8olClc.js} +1 -1
- package/dist/frontend/assets/channel-jdNM4v-F.js +1 -0
- package/dist/frontend/assets/{classDiagram-70f12bd4-DDFBNGUt.js → classDiagram-70f12bd4-D2U4i4yC.js} +1 -1
- package/dist/frontend/assets/{classDiagram-d40c83e7-cgC3s7Nz.js → classDiagram-d40c83e7-DtBHRJc9.js} +1 -1
- package/dist/frontend/assets/classDiagram-v2-d5a6b087-Drykcxns.js +2 -0
- package/dist/frontend/assets/classDiagram-v2-f2320105-D8ruMBA6.js +2 -0
- package/dist/frontend/assets/clone-DFO_zGRO.js +1 -0
- package/dist/frontend/assets/{createText-2e5e7dd3-CqY00v-4.js → createText-2e5e7dd3-1KPUhDpM.js} +1 -1
- package/dist/frontend/assets/{createText-d213de94-D59sBfqq.js → createText-d213de94-C6EhGbUN.js} +4 -4
- package/dist/frontend/assets/{cs-CZ-2BRQDIVT-CK09MAsg.js → cs-CZ-2BRQDIVT-DgjTgz6K.js} +1 -1
- package/dist/frontend/assets/{cytoscape-cose-bilkent-CKy_tuGZ.js → cytoscape-cose-bilkent-BnIHl72R.js} +1 -1
- package/dist/frontend/assets/{da-DK-5WZEPLOC-V4hdLKuT.js → da-DK-5WZEPLOC-uEs3xGKF.js} +1 -1
- package/dist/frontend/assets/{de-DE-XR44H4JA-Dw8qkP9g.js → de-DE-XR44H4JA-CBglLw42.js} +1 -1
- package/dist/frontend/assets/{edges-332bd1c7-D-u0KMZB.js → edges-332bd1c7-cWCBwcrg.js} +1 -1
- package/dist/frontend/assets/{edges-e0da2a9e-B3UFqLqY.js → edges-e0da2a9e-DKeDUJ_F.js} +1 -1
- package/dist/frontend/assets/{el-GR-BZB4AONW-BUdBQbYg.js → el-GR-BZB4AONW-B-_eub00.js} +1 -1
- package/dist/frontend/assets/{elk.bundled-BZA3Xcoo.js → elk.bundled-Ch9TeAu4.js} +1 -1
- package/dist/frontend/assets/{erDiagram-880f2ed8-CNxm7e1x.js → erDiagram-880f2ed8-CtDf_58r.js} +4 -4
- package/dist/frontend/assets/{erDiagram-9861fffd-C2u_ziAM.js → erDiagram-9861fffd-CAlUeljk.js} +1 -1
- package/dist/frontend/assets/{es-ES-U4NZUMDT-DS0FAZqX.js → es-ES-U4NZUMDT-BIN3bSVD.js} +1 -1
- package/dist/frontend/assets/{eu-ES-A7QVB2H4-D-D5uidc.js → eu-ES-A7QVB2H4-TyQet3-N.js} +1 -1
- package/dist/frontend/assets/{fa-IR-HGAKTJCU-mAW_2-jg.js → fa-IR-HGAKTJCU-DVmjjr57.js} +1 -1
- package/dist/frontend/assets/{fi-FI-Z5N7JZ37-B9mW6nxS.js → fi-FI-Z5N7JZ37-ZW9kXLa8.js} +1 -1
- package/dist/frontend/assets/{flowDb-7c981674-6JB2gd0I.js → flowDb-7c981674-B5ppx8EN.js} +1 -1
- package/dist/frontend/assets/{flowDb-956e92f1-BbpkWRi7.js → flowDb-956e92f1-Dqi00Wq-.js} +1 -1
- package/dist/frontend/assets/{flowDiagram-66a62f08-DrhrQuXc.js → flowDiagram-66a62f08-v3Liu0m_.js} +1 -1
- package/dist/frontend/assets/{flowDiagram-cbd28bf7-BmNY7bvJ.js → flowDiagram-cbd28bf7-DJ_YHRwa.js} +3 -3
- package/dist/frontend/assets/flowDiagram-v2-96b9c2cf-6vlgZ0Bg.js +1 -0
- package/dist/frontend/assets/flowDiagram-v2-ffc7f31a-FmnI78Vm.js +1 -0
- package/dist/frontend/assets/{flowchart-elk-definition-36e2d292-4G1YiIlk.js → flowchart-elk-definition-36e2d292-BleYul6b.js} +2 -2
- package/dist/frontend/assets/{flowchart-elk-definition-4a651766-Cm94SrrQ.js → flowchart-elk-definition-4a651766-D6lLh4Ih.js} +1 -1
- package/dist/frontend/assets/{fr-FR-RHASNOE6-BXnmUxTA.js → fr-FR-RHASNOE6-CxLR66tH.js} +1 -1
- package/dist/frontend/assets/{ganttDiagram-04f9e578-X0BweAnG.js → ganttDiagram-04f9e578-DJSOyr8B.js} +1 -1
- package/dist/frontend/assets/{ganttDiagram-c361ad54-D3vxZ5MQ.js → ganttDiagram-c361ad54-CJmbCLvp.js} +1 -1
- package/dist/frontend/assets/{gitGraphDiagram-21fc4d3e-wznrpRWn.js → gitGraphDiagram-21fc4d3e-DiRq_FJc.js} +6 -6
- package/dist/frontend/assets/{gitGraphDiagram-72cf32ee-B9zCS3Pw.js → gitGraphDiagram-72cf32ee-MfvDzoAb.js} +1 -1
- package/dist/frontend/assets/{gl-ES-HMX3MZ6V-CrUx-ab6.js → gl-ES-HMX3MZ6V-CxWj2itK.js} +1 -1
- package/dist/frontend/assets/{graph-BQZ6SIVr.js → graph-CT_r46y9.js} +1 -1
- package/dist/frontend/assets/{graph-Cda8xSTx.js → graph-DJt_Vezo.js} +1 -1
- package/dist/frontend/assets/{he-IL-6SHJWFNN-D33eaw6-.js → he-IL-6SHJWFNN-5v2WFGO3.js} +1 -1
- package/dist/frontend/assets/{hi-IN-IWLTKZ5I-DEnStF-g.js → hi-IN-IWLTKZ5I-BJGbvgRu.js} +1 -1
- package/dist/frontend/assets/{hu-HU-A5ZG7DT2-BjH2w8Sn.js → hu-HU-A5ZG7DT2-REQT2EcE.js} +1 -1
- package/dist/frontend/assets/{id-ID-SAP4L64H-Bc6R-JEz.js → id-ID-SAP4L64H-aZWQYWdg.js} +1 -1
- package/dist/frontend/assets/{index-3862675e-CpYfS3Db.js → index-3862675e-BgrvgbsM.js} +1 -1
- package/dist/frontend/assets/{index-6079d271-Y2bgyRpa.js → index-6079d271-Cp8jFHTC.js} +1 -1
- package/dist/frontend/assets/{index-D7eYnAQp.js → index-DBHHeSXy.js} +4 -4
- package/dist/frontend/assets/index-GTq6y6g6.js +349 -0
- package/dist/frontend/assets/{infoDiagram-4a4f5b27-Bm3Oq-Rq.js → infoDiagram-4a4f5b27-C1ZJJtWv.js} +3 -3
- package/dist/frontend/assets/{infoDiagram-f8f76790-oNVitBKM.js → infoDiagram-f8f76790-GeS0JKxo.js} +1 -1
- package/dist/frontend/assets/{it-IT-JPQ66NNP-BcxQABF2.js → it-IT-JPQ66NNP-EyeA2Ty1.js} +1 -1
- package/dist/frontend/assets/{ja-JP-DBVTYXUO-D1zIM-nt.js → ja-JP-DBVTYXUO-BLcE45D9.js} +1 -1
- package/dist/frontend/assets/{journeyDiagram-29694f62-CamPk_3P.js → journeyDiagram-29694f62-4Z_lpK4g.js} +5 -5
- package/dist/frontend/assets/{journeyDiagram-49397b02-D6w3tLtf.js → journeyDiagram-49397b02-ChwU3Zo6.js} +1 -1
- package/dist/frontend/assets/{kaa-6HZHGXH3-CoILysVg.js → kaa-6HZHGXH3-CkpTcX9X.js} +1 -1
- package/dist/frontend/assets/{kab-KAB-ZGHBKWFO-B9ZbybBt.js → kab-KAB-ZGHBKWFO-CChq0y6o.js} +1 -1
- package/dist/frontend/assets/{kk-KZ-P5N5QNE5-Dxi4GIQ7.js → kk-KZ-P5N5QNE5-CwtoTY4z.js} +1 -1
- package/dist/frontend/assets/{km-KH-HSX4SM5Z-DaJ7EtO2.js → km-KH-HSX4SM5Z-D26sdohj.js} +1 -1
- package/dist/frontend/assets/{ko-KR-MTYHY66A-DG57Ah12.js → ko-KR-MTYHY66A-DvnDcJQA.js} +1 -1
- package/dist/frontend/assets/{ku-TR-6OUDTVRD-DnDf9E8a.js → ku-TR-6OUDTVRD-qdHbLgKg.js} +1 -1
- package/dist/frontend/assets/{layout-Drr-66M9.js → layout-C-wUeNvb.js} +1 -1
- package/dist/frontend/assets/{layout-BsWfhUAF.js → layout-YRLNN4au.js} +1 -1
- package/dist/frontend/assets/{line-DwQObOhP.js → line-BEgMClKG.js} +1 -1
- package/dist/frontend/assets/{linear-C36MjJps.js → linear-DDHpSdYw.js} +1 -1
- package/dist/frontend/assets/{lt-LT-XHIRWOB4-D_zqS0yg.js → lt-LT-XHIRWOB4-DT4LYe_d.js} +1 -1
- package/dist/frontend/assets/{lv-LV-5QDEKY6T-owIsIh1b.js → lv-LV-5QDEKY6T-Dbc3ivwT.js} +1 -1
- package/dist/frontend/assets/{mindmap-definition-ac74a2e8-Cf9JbrBJ.js → mindmap-definition-ac74a2e8-CY2RhWUN.js} +6 -6
- package/dist/frontend/assets/{mindmap-definition-fc14e90a-BpsNCNCp.js → mindmap-definition-fc14e90a-VT4JDi6N.js} +1 -1
- package/dist/frontend/assets/{mr-IN-CRQNXWMA-CS9XUj74.js → mr-IN-CRQNXWMA-D75p1fVc.js} +1 -1
- package/dist/frontend/assets/{my-MM-5M5IBNSE-BKB060k9.js → my-MM-5M5IBNSE-DnEoBXyE.js} +1 -1
- package/dist/frontend/assets/{nb-NO-T6EIAALU-CkmLUrvm.js → nb-NO-T6EIAALU-BL4ThJxl.js} +1 -1
- package/dist/frontend/assets/{nl-NL-IS3SIHDZ-DdiLyJoz.js → nl-NL-IS3SIHDZ-u18nUblY.js} +1 -1
- package/dist/frontend/assets/{nn-NO-6E72VCQL-Yevp6f_Y.js → nn-NO-6E72VCQL-B9LJ0oXc.js} +1 -1
- package/dist/frontend/assets/{oc-FR-POXYY2M6-D_M6GHup.js → oc-FR-POXYY2M6-BKG5kIcO.js} +1 -1
- package/dist/frontend/assets/{pa-IN-N4M65BXN-1vuACN9n.js → pa-IN-N4M65BXN-CmpnjbkL.js} +1 -1
- package/dist/frontend/assets/{pica-DDDx0iqx.js → pica-BcpcM0v1.js} +1 -1
- package/dist/frontend/assets/{pie-CFIpuQMO.js → pie-Swzl5NrI.js} +1 -1
- package/dist/frontend/assets/{pieDiagram-421022e6-DO7e_Qgw.js → pieDiagram-421022e6-ndz96BLe.js} +5 -5
- package/dist/frontend/assets/{pieDiagram-8a3498a8-BEoqChXo.js → pieDiagram-8a3498a8-D5rcV68O.js} +1 -1
- package/dist/frontend/assets/{pl-PL-T2D74RX3-Cc6Q5nSh.js → pl-PL-T2D74RX3-CrR66Rgj.js} +1 -1
- package/dist/frontend/assets/{pt-BR-5N22H2LF-3eEiAax3.js → pt-BR-5N22H2LF-Crv7jZiH.js} +1 -1
- package/dist/frontend/assets/{pt-PT-UZXXM6DQ-CgnF0Le7.js → pt-PT-UZXXM6DQ-Bkaa5ESR.js} +1 -1
- package/dist/frontend/assets/{quadrantDiagram-0957ecba-C1Eo-ZUY.js → quadrantDiagram-0957ecba-DOdyxeTP.js} +3 -3
- package/dist/frontend/assets/{quadrantDiagram-120e2f19-Bdfsl_O7.js → quadrantDiagram-120e2f19-beQD7R1C.js} +1 -1
- package/dist/frontend/assets/relay-BIOL2uw2.js +1 -0
- package/dist/frontend/assets/{requirementDiagram-23d650b8-DiOK6QW3.js → requirementDiagram-23d650b8-B2i-OB97.js} +1 -1
- package/dist/frontend/assets/{requirementDiagram-deff3bca-BDJ-7T4h.js → requirementDiagram-deff3bca-beS-SvfY.js} +1 -1
- package/dist/frontend/assets/{ro-RO-JPDTUUEW-CbJ3IB7a.js → ro-RO-JPDTUUEW-DsrnQm7K.js} +1 -1
- package/dist/frontend/assets/{ru-RU-B4JR7IUQ-PQGmeGG4.js → ru-RU-B4JR7IUQ-DrnGGdUs.js} +1 -1
- package/dist/frontend/assets/{sankeyDiagram-04a897e0-DV25bgN2.js → sankeyDiagram-04a897e0-CVad45bN.js} +1 -1
- package/dist/frontend/assets/{sankeyDiagram-23345273-szSb_Sei.js → sankeyDiagram-23345273-ghWBXXF2.js} +1 -1
- package/dist/frontend/assets/{selectAll-DsrPvhqV.js → selectAll-XplaO988.js} +1 -1
- package/dist/frontend/assets/{sequenceDiagram-17ac3bff-Dg88bSlc.js → sequenceDiagram-17ac3bff-DBI-EdWx.js} +1 -1
- package/dist/frontend/assets/{sequenceDiagram-704730f1-DRIjLCxx.js → sequenceDiagram-704730f1-BnJ__mBt.js} +1 -1
- package/dist/frontend/assets/{si-LK-N5RQ5JYF-BtIIudm3.js → si-LK-N5RQ5JYF-BxAwuRM5.js} +1 -1
- package/dist/frontend/assets/{sk-SK-C5VTKIMK-CIwXticU.js → sk-SK-C5VTKIMK-CnDyiLRf.js} +1 -1
- package/dist/frontend/assets/{sl-SI-NN7IZMDC-CXEdg57y.js → sl-SI-NN7IZMDC-BXHXIdFA.js} +1 -1
- package/dist/frontend/assets/{stateDiagram-587899a1-BwRwgRo6.js → stateDiagram-587899a1-D3Ahxsgc.js} +1 -1
- package/dist/frontend/assets/{stateDiagram-9c5f0230-C8n96rER.js → stateDiagram-9c5f0230-8XbLWvC0.js} +1 -1
- package/dist/frontend/assets/{stateDiagram-v2-51a3dcff-B7e5E4J7.js → stateDiagram-v2-51a3dcff-Cqf-MUGD.js} +1 -1
- package/dist/frontend/assets/{stateDiagram-v2-d93cdb3a-B18iZQLw.js → stateDiagram-v2-d93cdb3a-DjGrud9D.js} +1 -1
- package/dist/frontend/assets/{styles-2ab5d517-CfWubwio.js → styles-2ab5d517-B4qdHOgu.js} +3 -3
- package/dist/frontend/assets/{styles-5f03d8d2-C-_n19AB.js → styles-5f03d8d2-DOD2v5V8.js} +3 -3
- package/dist/frontend/assets/{styles-6aaf32cf-Dh-DHkVP.js → styles-6aaf32cf-bP3XXcmf.js} +1 -1
- package/dist/frontend/assets/{styles-9a916d00-Boy0hUEG.js → styles-9a916d00-QLeuL_Dy.js} +1 -1
- package/dist/frontend/assets/{styles-c10674c1-BfxiK3Ns.js → styles-c10674c1-Lpvh1u4f.js} +1 -1
- package/dist/frontend/assets/{styles-edf9a4b0-T7mKViRF.js → styles-edf9a4b0-D4W9aPm7.js} +1 -1
- package/dist/frontend/assets/subset-shared.chunk.js +9 -9
- package/dist/frontend/assets/subset-worker.chunk.js +1 -1
- package/dist/frontend/assets/{sv-SE-XGPEYMSR-dVrUAqld.js → sv-SE-XGPEYMSR-YrCQQBuo.js} +1 -1
- package/dist/frontend/assets/{svgDrawCommon-08f97a94-fsWk6SQc.js → svgDrawCommon-08f97a94-CwNv3PjS.js} +1 -1
- package/dist/frontend/assets/{svgDrawCommon-3ba9043b-BJ8X3qS-.js → svgDrawCommon-3ba9043b-DZbT0Bqu.js} +1 -1
- package/dist/frontend/assets/{ta-IN-2NMHFXQM-CtVVZfIT.js → ta-IN-2NMHFXQM-qR82aBSW.js} +1 -1
- package/dist/frontend/assets/{th-TH-HPSO5L25-Do90CyDN.js → th-TH-HPSO5L25-C65V7hE5.js} +1 -1
- package/dist/frontend/assets/{timeline-definition-7e6b55e7-D_iHA485.js → timeline-definition-7e6b55e7-GkPy3QeG.js} +3 -3
- package/dist/frontend/assets/{timeline-definition-85554ec2-nhT8QMsO.js → timeline-definition-85554ec2-CCdkBaC0.js} +3 -3
- package/dist/frontend/assets/{tr-TR-DEFEU3FU-MfR-IQke.js → tr-TR-DEFEU3FU-CFzbwAKV.js} +1 -1
- package/dist/frontend/assets/{uk-UA-QMV73CPH-CPet8B1O.js → uk-UA-QMV73CPH-ePt5WVz3.js} +1 -1
- package/dist/frontend/assets/{union-BgYwhB7T.js → union-CVZobYBh.js} +1 -1
- package/dist/frontend/assets/{vi-VN-M7AON7JQ-3HD06T_8.js → vi-VN-M7AON7JQ-CaPKykgJ.js} +1 -1
- package/dist/frontend/assets/{xychartDiagram-b6496bcd-D5vSaFT2.js → xychartDiagram-b6496bcd-CV5WRc81.js} +4 -4
- package/dist/frontend/assets/{xychartDiagram-e933f94c-DLIHXsUi.js → xychartDiagram-e933f94c-BH-ySdQf.js} +1 -1
- package/dist/frontend/assets/{zh-CN-LNUGB5OW-t2RyWPw2.js → zh-CN-LNUGB5OW-CK8AEj8o.js} +1 -1
- package/dist/frontend/assets/{zh-HK-E62DVLB3-Bj22hc7e.js → zh-HK-E62DVLB3-CUINiFgH.js} +1 -1
- package/dist/frontend/assets/{zh-TW-RAJ6MFWO-CBRhqhU1.js → zh-TW-RAJ6MFWO-BEBY7iJR.js} +1 -1
- package/dist/frontend/assets/zipObject-W8SLG3F1.js +1 -0
- package/dist/frontend/index.html +167 -235
- package/dist/{index.d.ts → mcp.d.ts} +1 -1
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +1183 -0
- package/dist/mcp.js.map +1 -0
- package/package.json +19 -34
- package/skills/excalidraw-skill/SKILL.md +42 -55
- package/skills/excalidraw-skill/references/cheatsheet.md +23 -56
- package/skills/excalidraw-skill/scripts/clear-canvas.cjs +9 -32
- package/skills/excalidraw-skill/scripts/create-element.cjs +18 -61
- package/skills/excalidraw-skill/scripts/delete-element.cjs +12 -42
- package/skills/excalidraw-skill/scripts/export-elements.cjs +17 -44
- package/skills/excalidraw-skill/scripts/gh-helper.cjs +58 -0
- package/skills/excalidraw-skill/scripts/healthcheck.cjs +8 -29
- package/skills/excalidraw-skill/scripts/import-elements.cjs +20 -70
- package/skills/excalidraw-skill/scripts/update-element.cjs +19 -63
- package/tsconfig.json +3 -2
- package/vite.config.js +3 -10
- package/dist/frontend/assets/arc-Cc3ncJ1n.js +0 -1
- package/dist/frontend/assets/channel-BQSIb3i3.js +0 -1
- package/dist/frontend/assets/classDiagram-v2-d5a6b087-BgB8AG2e.js +0 -2
- package/dist/frontend/assets/classDiagram-v2-f2320105-Dtywkpeg.js +0 -2
- package/dist/frontend/assets/clone-BoZziNL9.js +0 -1
- package/dist/frontend/assets/flowDiagram-v2-96b9c2cf-Z3H2d3Wj.js +0 -1
- package/dist/frontend/assets/flowDiagram-v2-ffc7f31a-CIQOMghC.js +0 -1
- package/dist/frontend/assets/index-DIz4WrSB.js +0 -349
- package/dist/frontend/assets/zipObject-DMfHW2o8.js +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -2279
- package/dist/index.js.map +0 -1
- package/dist/remote.d.ts +0 -5
- package/dist/remote.d.ts.map +0 -1
- package/dist/remote.js +0 -129
- package/dist/remote.js.map +0 -1
- package/dist/server.d.ts +0 -4
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js +0 -1208
- package/dist/server.js.map +0 -1
- package/dist/target.d.ts +0 -38
- package/dist/target.d.ts.map +0 -1
- package/dist/target.js +0 -225
- package/dist/target.js.map +0 -1
- package/dist/types.d.ts +0 -228
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -45
- package/dist/types.js.map +0 -1
- package/dist/utils/auth.d.ts +0 -14
- package/dist/utils/auth.d.ts.map +0 -1
- package/dist/utils/auth.js +0 -67
- package/dist/utils/auth.js.map +0 -1
- package/dist/utils/github.d.ts +0 -24
- package/dist/utils/github.d.ts.map +0 -1
- package/dist/utils/github.js +0 -132
- package/dist/utils/github.js.map +0 -1
- package/dist/utils/logger.d.ts +0 -4
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js +0 -25
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/r2store.d.ts +0 -12
- package/dist/utils/r2store.d.ts.map +0 -1
- package/dist/utils/r2store.js +0 -75
- package/dist/utils/r2store.js.map +0 -1
- package/frontend/drop.png +0 -0
- package/frontend/index.html +0 -339
- package/frontend/src/App.tsx +0 -1091
- package/frontend/src/main.tsx +0 -15
- package/frontend/src/utils/ghSync.ts +0 -234
- package/frontend/src/utils/mermaidConverter.ts +0 -43
- package/frontend/src/utils/webmcp.ts +0 -265
- package/scripts/check-local-bind.mjs +0 -164
- package/scripts/postinstall.mjs +0 -23
- package/scripts/publish-pages.sh +0 -39
- package/scripts/seed-r2.mjs +0 -9
- package/src/cli.ts +0 -528
- package/src/index.ts +0 -2538
- package/src/remote.ts +0 -92
- package/src/server.ts +0 -1349
- package/src/target.ts +0 -198
- package/src/types.ts +0 -336
- package/src/utils/auth.ts +0 -61
- package/src/utils/github.ts +0 -121
- package/src/utils/logger.ts +0 -35
- package/src/utils/r2store.ts +0 -70
package/src/target.ts
DELETED
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { execSync } from 'node:child_process';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import logger from './utils/logger.js';
|
|
7
|
-
import { generateId, ServerElement } from './types.js';
|
|
8
|
-
|
|
9
|
-
export type Target = { kind: 'local' } | { kind: 'remote'; repo: string };
|
|
10
|
-
let active: Target = { kind: 'local' };
|
|
11
|
-
export const getTarget = (): Target => active;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export const APP_CLIENT_ID = process.env.GITHUB_OAUTH_CLIENT_ID || 'Iv23liuS2fx3QOEIoDmx';
|
|
15
|
-
|
|
16
|
-
const CONFIG_NAME = '.excalidrop.json';
|
|
17
|
-
|
|
18
|
-
function findConfigDir(): string | null {
|
|
19
|
-
let dir = process.cwd();
|
|
20
|
-
const root = path.parse(dir).root;
|
|
21
|
-
while (true) {
|
|
22
|
-
try {
|
|
23
|
-
const raw = JSON.parse(fs.readFileSync(path.join(dir, CONFIG_NAME), 'utf8'));
|
|
24
|
-
if (raw && typeof raw === 'object') return dir;
|
|
25
|
-
} catch { /* keep walking */ }
|
|
26
|
-
if (dir === root) return null;
|
|
27
|
-
dir = path.dirname(dir);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function persistTarget(repo: string | null): void {
|
|
32
|
-
const dir = findConfigDir();
|
|
33
|
-
if (!dir) return;
|
|
34
|
-
try {
|
|
35
|
-
const file = path.join(dir, CONFIG_NAME);
|
|
36
|
-
const raw = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
37
|
-
if (repo) raw.remote = repo; else delete raw.remote;
|
|
38
|
-
fs.writeFileSync(file, JSON.stringify(raw, null, 2) + '\n');
|
|
39
|
-
} catch (e) { logger.warn('could not persist remote target: ' + (e as Error).message); }
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function loadSavedTarget(): string | null {
|
|
43
|
-
const dir = findConfigDir();
|
|
44
|
-
if (!dir) return null;
|
|
45
|
-
try {
|
|
46
|
-
const raw = JSON.parse(fs.readFileSync(path.join(dir, CONFIG_NAME), 'utf8'));
|
|
47
|
-
return typeof raw.remote === 'string' ? raw.remote : null;
|
|
48
|
-
} catch { return null; }
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const TOKEN_FILE = path.join(os.homedir(), '.config', 'excalidrop', 'gh_token');
|
|
52
|
-
export function resolveToken(): string {
|
|
53
|
-
if (process.env.GITHUB_TOKEN) return process.env.GITHUB_TOKEN;
|
|
54
|
-
try {
|
|
55
|
-
const t = execSync('gh auth token', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
56
|
-
if (t) return t;
|
|
57
|
-
} catch { /* no gh */ }
|
|
58
|
-
try {
|
|
59
|
-
const t = fs.readFileSync(TOKEN_FILE, 'utf8').trim();
|
|
60
|
-
if (t) return t;
|
|
61
|
-
} catch { /* no stored token */ }
|
|
62
|
-
return '';
|
|
63
|
-
}
|
|
64
|
-
export function storeToken(t: string): void {
|
|
65
|
-
fs.mkdirSync(path.dirname(TOKEN_FILE), { recursive: true });
|
|
66
|
-
fs.writeFileSync(TOKEN_FILE, t + '\n', { mode: 0o600 });
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
export function parseRepo(input: string): string {
|
|
71
|
-
const s = input.trim().replace(/\/$/, '');
|
|
72
|
-
const m = s.match(/^https?:\/\/([a-z0-9-]+)\.github\.io\/([a-z0-9_.-]+)/i);
|
|
73
|
-
if (m) return `${m[1]}/${m[2]}`.toLowerCase();
|
|
74
|
-
if (/^[a-z0-9_.-]+\/[a-z0-9_.-]+$/i.test(s)) return s.toLowerCase();
|
|
75
|
-
throw new Error(`Cannot parse repo from "${input}" — use owner/repo or a *.github.io/<repo> URL`);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const scenes = new Map<string, { elements: Map<string, ServerElement>; sha: string | null; dirty: boolean }>();
|
|
80
|
-
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
81
|
-
const SCENE_PATH = process.env.SCENE_PATH || 'canvas.excalidraw';
|
|
82
|
-
|
|
83
|
-
function headers(): Record<string, string> {
|
|
84
|
-
return { Authorization: `Bearer ${resolveToken()}`, Accept: 'application/vnd.github+json', 'User-Agent': 'excalidrop', 'Content-Type': 'application/json' };
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export async function switchRemote(input?: string): Promise<{ target: Target; elements: number }> {
|
|
88
|
-
if (!input) { active = { kind: 'local' }; persistTarget(null); return { target: active, elements: 0 }; }
|
|
89
|
-
const repo = parseRepo(input);
|
|
90
|
-
active = { kind: 'remote', repo };
|
|
91
|
-
const st = await ensureLoaded(repo);
|
|
92
|
-
persistTarget(repo);
|
|
93
|
-
return { target: active, elements: st.elements.size };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async function ensureLoaded(repo: string) {
|
|
97
|
-
let st = scenes.get(repo);
|
|
98
|
-
if (st) return st;
|
|
99
|
-
const tok = resolveToken();
|
|
100
|
-
if (!tok) throw new Error('No GitHub token. Run github_login or `gh auth login` first.');
|
|
101
|
-
const r = await fetch(`https://api.github.com/repos/${repo}/contents/${SCENE_PATH}?ref=main`, { headers: headers(), signal: AbortSignal.timeout(20000) });
|
|
102
|
-
st = { elements: new Map(), sha: null, dirty: false };
|
|
103
|
-
if (r.ok) {
|
|
104
|
-
const j = await r.json() as any;
|
|
105
|
-
const doc = JSON.parse(Buffer.from(j.content, 'base64').toString('utf8'));
|
|
106
|
-
for (const el of doc.elements || []) st.elements.set(el.id, el);
|
|
107
|
-
st.sha = j.sha;
|
|
108
|
-
}
|
|
109
|
-
scenes.set(repo, st);
|
|
110
|
-
return st;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function current(): { elements: Map<string, ServerElement>; sha: string | null; dirty: boolean } {
|
|
114
|
-
if (active.kind !== 'remote') throw new Error('not in remote mode');
|
|
115
|
-
const st = scenes.get(active.repo);
|
|
116
|
-
if (!st) throw new Error('call switch_remote first');
|
|
117
|
-
return st;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function scheduleCommit(): void {
|
|
121
|
-
if (active.kind !== 'remote') return;
|
|
122
|
-
if (timer) clearTimeout(timer);
|
|
123
|
-
timer = setTimeout(() => { void commitNow('excalidrop: autosync from MCP'); }, 10000);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export async function commitNow(message?: string): Promise<{ sha: string; count: number }> {
|
|
127
|
-
if (active.kind !== 'remote') throw new Error('not in remote mode');
|
|
128
|
-
const repo = active.repo;
|
|
129
|
-
const st = current();
|
|
130
|
-
const elements = Array.from(st.elements.values());
|
|
131
|
-
const doc = { type: 'excalidraw', version: 2, source: 'excalidrop', elements };
|
|
132
|
-
const gh = await import('./utils/github.js');
|
|
133
|
-
const newSha = await gh.putFile(repo, SCENE_PATH, doc, message || `excalidrop: update ${elements.length} elements`, 'main', st.sha || undefined);
|
|
134
|
-
st.sha = newSha;
|
|
135
|
-
st.dirty = false;
|
|
136
|
-
try {
|
|
137
|
-
const cur = await gh.getFile(repo, SCENE_PATH, 'gh-pages').catch(() => null);
|
|
138
|
-
await gh.putFile(repo, SCENE_PATH, doc, 'excalidrop: sync viewer', 'gh-pages', cur?.sha);
|
|
139
|
-
await gh.updateRepoMetadata(repo, { homepage: `https://${repo.replace('/', '.github.io/')}/` });
|
|
140
|
-
} catch (e) { logger.warn('viewer mirror failed: ' + (e as Error).message); }
|
|
141
|
-
logger.info(`Committed ${elements.length} elements to ${repo}`);
|
|
142
|
-
return { sha: st.sha as string, count: elements.length };
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
export function rCreate(el: ServerElement): ServerElement {
|
|
147
|
-
const st = current();
|
|
148
|
-
const full = { ...el, id: el.id || generateId() };
|
|
149
|
-
st.elements.set(full.id, full); st.dirty = true; scheduleCommit();
|
|
150
|
-
return full;
|
|
151
|
-
}
|
|
152
|
-
export function rBatch(els: ServerElement[]): ServerElement[] { return els.map(rCreate); }
|
|
153
|
-
export function rUpdate(id: string, updates: Partial<ServerElement>): ServerElement {
|
|
154
|
-
const st = current();
|
|
155
|
-
const cur = st.elements.get(id);
|
|
156
|
-
if (!cur) throw new Error(`Element ${id} not found`);
|
|
157
|
-
const u = { ...cur, ...updates };
|
|
158
|
-
st.elements.set(id, u); st.dirty = true; scheduleCommit();
|
|
159
|
-
return u;
|
|
160
|
-
}
|
|
161
|
-
export function rDelete(id: string): void { const st = current(); st.elements.delete(id); st.dirty = true; scheduleCommit(); }
|
|
162
|
-
export function rGet(id: string): ServerElement | null { return current().elements.get(id) || null; }
|
|
163
|
-
export function rList(type?: string): ServerElement[] {
|
|
164
|
-
return Array.from(current().elements.values()).filter(e => !type || e.type === type);
|
|
165
|
-
}
|
|
166
|
-
export function rClear(): number { const st = current(); const n = st.elements.size; st.elements.clear(); st.dirty = true; scheduleCommit(); return n; }
|
|
167
|
-
export const isRemote = (): boolean => active.kind === 'remote';
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
export async function deviceStart(clientId: string): Promise<{ user_code: string; verification_uri: string; device_code: string; interval: number }> {
|
|
171
|
-
const r = await fetch('https://github.com/login/device/code', {
|
|
172
|
-
method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
|
|
173
|
-
body: JSON.stringify({ client_id: clientId }),
|
|
174
|
-
});
|
|
175
|
-
if (!r.ok) throw new Error('device flow start failed');
|
|
176
|
-
return r.json() as any;
|
|
177
|
-
}
|
|
178
|
-
export async function repoId(owner: string, repo: string): Promise<number | null> {
|
|
179
|
-
try {
|
|
180
|
-
const r = await fetch(`https://api.github.com/repos/${owner}/${repo}`, { headers: { Accept: 'application/vnd.github+json', 'User-Agent': 'excalidrop' } });
|
|
181
|
-
if (!r.ok) return null;
|
|
182
|
-
return ((await r.json()) as any).id ?? null;
|
|
183
|
-
} catch { return null; }
|
|
184
|
-
}
|
|
185
|
-
export async function devicePoll(clientId: string, deviceCode: string, intervalSec: number, timeoutSec = 300, repositoryId?: number): Promise<string> {
|
|
186
|
-
const deadline = Date.now() + timeoutSec * 1000;
|
|
187
|
-
while (Date.now() < deadline) {
|
|
188
|
-
await new Promise(r => setTimeout(r, intervalSec * 1000));
|
|
189
|
-
const r = await fetch('https://github.com/login/oauth/access_token', {
|
|
190
|
-
method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
|
|
191
|
-
body: JSON.stringify({ client_id: clientId, device_code: deviceCode, grant_type: 'urn:ietf:params:oauth:grant-type:device_code', ...(repositoryId ? { repository_id: repositoryId } : {}) }),
|
|
192
|
-
});
|
|
193
|
-
const j = await r.json() as any;
|
|
194
|
-
if (j.access_token) { storeToken(j.access_token); return j.access_token; }
|
|
195
|
-
if (j.error && j.error !== 'authorization_pending' && j.error !== 'slow_down') throw new Error(j.error_description || j.error);
|
|
196
|
-
}
|
|
197
|
-
throw new Error('device login timed out');
|
|
198
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
export interface ExcalidrawElementBase {
|
|
2
|
-
id: string;
|
|
3
|
-
type: ExcalidrawElementType;
|
|
4
|
-
x: number;
|
|
5
|
-
y: number;
|
|
6
|
-
width?: number;
|
|
7
|
-
height?: number;
|
|
8
|
-
angle?: number;
|
|
9
|
-
strokeColor?: string;
|
|
10
|
-
backgroundColor?: string;
|
|
11
|
-
fillStyle?: string;
|
|
12
|
-
strokeWidth?: number;
|
|
13
|
-
strokeStyle?: string;
|
|
14
|
-
roughness?: number;
|
|
15
|
-
opacity?: number;
|
|
16
|
-
groupIds?: string[];
|
|
17
|
-
frameId?: string | null;
|
|
18
|
-
roundness?: {
|
|
19
|
-
type: number;
|
|
20
|
-
value?: number;
|
|
21
|
-
} | null;
|
|
22
|
-
seed?: number;
|
|
23
|
-
versionNonce?: number;
|
|
24
|
-
isDeleted?: boolean;
|
|
25
|
-
locked?: boolean;
|
|
26
|
-
link?: string | null;
|
|
27
|
-
customData?: Record<string, any> | null;
|
|
28
|
-
boundElements?: readonly ExcalidrawBoundElement[] | null;
|
|
29
|
-
updated?: number;
|
|
30
|
-
containerId?: string | null;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface ExcalidrawTextElement extends ExcalidrawElementBase {
|
|
34
|
-
type: 'text';
|
|
35
|
-
text: string;
|
|
36
|
-
fontSize?: number;
|
|
37
|
-
fontFamily?: number;
|
|
38
|
-
textAlign?: string;
|
|
39
|
-
verticalAlign?: string;
|
|
40
|
-
baseline?: number;
|
|
41
|
-
lineHeight?: number;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface ExcalidrawRectangleElement extends ExcalidrawElementBase {
|
|
45
|
-
type: 'rectangle';
|
|
46
|
-
width: number;
|
|
47
|
-
height: number;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface ExcalidrawEllipseElement extends ExcalidrawElementBase {
|
|
51
|
-
type: 'ellipse';
|
|
52
|
-
width: number;
|
|
53
|
-
height: number;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface ExcalidrawDiamondElement extends ExcalidrawElementBase {
|
|
57
|
-
type: 'diamond';
|
|
58
|
-
width: number;
|
|
59
|
-
height: number;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export interface ExcalidrawArrowElement extends ExcalidrawElementBase {
|
|
63
|
-
type: 'arrow';
|
|
64
|
-
points: readonly [number, number][];
|
|
65
|
-
lastCommittedPoint?: readonly [number, number] | null;
|
|
66
|
-
startBinding?: ExcalidrawBinding | null;
|
|
67
|
-
endBinding?: ExcalidrawBinding | null;
|
|
68
|
-
startArrowhead?: string | null;
|
|
69
|
-
endArrowhead?: string | null;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export interface ExcalidrawLineElement extends ExcalidrawElementBase {
|
|
73
|
-
type: 'line';
|
|
74
|
-
points: readonly [number, number][];
|
|
75
|
-
lastCommittedPoint?: readonly [number, number] | null;
|
|
76
|
-
startBinding?: ExcalidrawBinding | null;
|
|
77
|
-
endBinding?: ExcalidrawBinding | null;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export interface ExcalidrawFreedrawElement extends ExcalidrawElementBase {
|
|
81
|
-
type: 'freedraw';
|
|
82
|
-
points: readonly [number, number][];
|
|
83
|
-
pressures?: readonly number[];
|
|
84
|
-
simulatePressure?: boolean;
|
|
85
|
-
lastCommittedPoint?: readonly [number, number] | null;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export type ExcalidrawElement =
|
|
89
|
-
| ExcalidrawTextElement
|
|
90
|
-
| ExcalidrawRectangleElement
|
|
91
|
-
| ExcalidrawEllipseElement
|
|
92
|
-
| ExcalidrawDiamondElement
|
|
93
|
-
| ExcalidrawArrowElement
|
|
94
|
-
| ExcalidrawLineElement
|
|
95
|
-
| ExcalidrawFreedrawElement;
|
|
96
|
-
|
|
97
|
-
export interface ExcalidrawBoundElement {
|
|
98
|
-
id: string;
|
|
99
|
-
type: 'text' | 'arrow';
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export interface ExcalidrawBinding {
|
|
103
|
-
elementId: string;
|
|
104
|
-
focus: number;
|
|
105
|
-
gap: number;
|
|
106
|
-
fixedPoint?: readonly [number, number] | null;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export type ExcalidrawElementType = 'rectangle' | 'ellipse' | 'diamond' | 'arrow' | 'text' | 'line' | 'freedraw' | 'image';
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
export const EXCALIDRAW_ELEMENT_TYPES: Record<string, ExcalidrawElementType> = {
|
|
113
|
-
RECTANGLE: 'rectangle',
|
|
114
|
-
ELLIPSE: 'ellipse',
|
|
115
|
-
DIAMOND: 'diamond',
|
|
116
|
-
ARROW: 'arrow',
|
|
117
|
-
TEXT: 'text',
|
|
118
|
-
FREEDRAW: 'freedraw',
|
|
119
|
-
LINE: 'line',
|
|
120
|
-
IMAGE: 'image'
|
|
121
|
-
} as const;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
export interface ServerElement extends Omit<ExcalidrawElementBase, 'id'> {
|
|
125
|
-
id: string;
|
|
126
|
-
type: ExcalidrawElementType;
|
|
127
|
-
createdAt?: string;
|
|
128
|
-
updatedAt?: string;
|
|
129
|
-
version?: number;
|
|
130
|
-
syncedAt?: string;
|
|
131
|
-
source?: string;
|
|
132
|
-
syncTimestamp?: string;
|
|
133
|
-
text?: string;
|
|
134
|
-
originalText?: string;
|
|
135
|
-
fontSize?: number;
|
|
136
|
-
fontFamily?: string | number;
|
|
137
|
-
label?: {
|
|
138
|
-
text: string;
|
|
139
|
-
};
|
|
140
|
-
points?: any;
|
|
141
|
-
|
|
142
|
-
start?: { id: string };
|
|
143
|
-
end?: { id: string };
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
export interface ApiResponse<T = any> {
|
|
148
|
-
success: boolean;
|
|
149
|
-
data?: T;
|
|
150
|
-
error?: string;
|
|
151
|
-
message?: string;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export interface ElementsResponse extends ApiResponse {
|
|
155
|
-
elements: ServerElement[];
|
|
156
|
-
count: number;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export interface ElementResponse extends ApiResponse {
|
|
160
|
-
element: ServerElement;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export interface SyncResponse extends ApiResponse {
|
|
164
|
-
count: number;
|
|
165
|
-
syncedAt: string;
|
|
166
|
-
beforeCount: number;
|
|
167
|
-
afterCount: number;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
export interface WebSocketMessage {
|
|
172
|
-
type: WebSocketMessageType;
|
|
173
|
-
[key: string]: any;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export type WebSocketMessageType =
|
|
177
|
-
| 'initial_elements'
|
|
178
|
-
| 'element_created'
|
|
179
|
-
| 'element_updated'
|
|
180
|
-
| 'element_deleted'
|
|
181
|
-
| 'elements_batch_created'
|
|
182
|
-
| 'elements_synced'
|
|
183
|
-
| 'sync_status'
|
|
184
|
-
| 'mermaid_convert'
|
|
185
|
-
| 'canvas_cleared'
|
|
186
|
-
| 'export_image_request'
|
|
187
|
-
| 'set_viewport'
|
|
188
|
-
| 'files_added'
|
|
189
|
-
| 'file_deleted';
|
|
190
|
-
|
|
191
|
-
export interface InitialElementsMessage extends WebSocketMessage {
|
|
192
|
-
type: 'initial_elements';
|
|
193
|
-
elements: ServerElement[];
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
export interface ElementCreatedMessage extends WebSocketMessage {
|
|
197
|
-
type: 'element_created';
|
|
198
|
-
element: ServerElement;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export interface ElementUpdatedMessage extends WebSocketMessage {
|
|
202
|
-
type: 'element_updated';
|
|
203
|
-
element: ServerElement;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export interface ElementDeletedMessage extends WebSocketMessage {
|
|
207
|
-
type: 'element_deleted';
|
|
208
|
-
elementId: string;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export interface BatchCreatedMessage extends WebSocketMessage {
|
|
212
|
-
type: 'elements_batch_created';
|
|
213
|
-
elements: ServerElement[];
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
export interface SyncStatusMessage extends WebSocketMessage {
|
|
217
|
-
type: 'sync_status';
|
|
218
|
-
elementCount: number;
|
|
219
|
-
timestamp: string;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
export interface MermaidConvertMessage extends WebSocketMessage {
|
|
223
|
-
type: 'mermaid_convert';
|
|
224
|
-
mermaidDiagram: string;
|
|
225
|
-
config?: MermaidConfig;
|
|
226
|
-
timestamp: string;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
export interface MermaidConfig {
|
|
231
|
-
startOnLoad?: boolean;
|
|
232
|
-
flowchart?: {
|
|
233
|
-
curve?: 'linear' | 'basis';
|
|
234
|
-
};
|
|
235
|
-
themeVariables?: {
|
|
236
|
-
fontSize?: string;
|
|
237
|
-
};
|
|
238
|
-
maxEdges?: number;
|
|
239
|
-
maxTextSize?: number;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
export interface MermaidConversionRequest {
|
|
243
|
-
mermaidDiagram: string;
|
|
244
|
-
config?: MermaidConfig;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
export interface MermaidConversionResponse extends ApiResponse {
|
|
248
|
-
elements: ServerElement[];
|
|
249
|
-
files?: any;
|
|
250
|
-
count: number;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
export interface CanvasClearedMessage extends WebSocketMessage {
|
|
255
|
-
type: 'canvas_cleared';
|
|
256
|
-
timestamp: string;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
export interface ExportImageRequestMessage extends WebSocketMessage {
|
|
261
|
-
type: 'export_image_request';
|
|
262
|
-
requestId: string;
|
|
263
|
-
format: 'png' | 'svg';
|
|
264
|
-
background?: boolean;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
export interface SetViewportMessage extends WebSocketMessage {
|
|
269
|
-
type: 'set_viewport';
|
|
270
|
-
requestId: string;
|
|
271
|
-
scrollToContent?: boolean;
|
|
272
|
-
scrollToElementId?: string;
|
|
273
|
-
zoom?: number;
|
|
274
|
-
offsetX?: number;
|
|
275
|
-
offsetY?: number;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
export interface Snapshot {
|
|
280
|
-
name: string;
|
|
281
|
-
elements: ServerElement[];
|
|
282
|
-
createdAt: string;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
export const elements = new Map<string, ServerElement>();
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
export const snapshots = new Map<string, Snapshot>();
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
export interface ExcalidrawFile {
|
|
293
|
-
id: string;
|
|
294
|
-
dataURL: string;
|
|
295
|
-
mimeType: string;
|
|
296
|
-
created: number;
|
|
297
|
-
}
|
|
298
|
-
export const files = new Map<string, ExcalidrawFile>();
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
export function validateElement(element: Partial<ServerElement>): element is ServerElement {
|
|
302
|
-
const requiredFields: (keyof ServerElement)[] = ['type', 'x', 'y'];
|
|
303
|
-
const hasRequiredFields = requiredFields.every(field => field in element);
|
|
304
|
-
|
|
305
|
-
if (!hasRequiredFields) {
|
|
306
|
-
throw new Error(`Missing required fields: ${requiredFields.join(', ')}`);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
if (!Object.values(EXCALIDRAW_ELEMENT_TYPES).includes(element.type as ExcalidrawElementType)) {
|
|
310
|
-
throw new Error(`Invalid element type: ${element.type}`);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return true;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
export function generateId(): string {
|
|
318
|
-
return Date.now().toString(36) + Math.random().toString(36).substring(2);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
export function normalizeFontFamily(fontFamily: string | number | undefined): number | undefined {
|
|
323
|
-
if (fontFamily === undefined) return undefined;
|
|
324
|
-
if (typeof fontFamily === 'number') return fontFamily;
|
|
325
|
-
const map: Record<string, number> = {
|
|
326
|
-
'virgil': 1, 'hand': 1, 'handwritten': 1,
|
|
327
|
-
'helvetica': 2, 'sans': 2, 'sans-serif': 2,
|
|
328
|
-
'cascadia': 3, 'mono': 3, 'monospace': 3,
|
|
329
|
-
'excalifont': 5,
|
|
330
|
-
'nunito': 6,
|
|
331
|
-
'lilita': 7, 'lilita one': 7,
|
|
332
|
-
'comic shanns': 8, 'comic': 8,
|
|
333
|
-
'1': 1, '2': 2, '3': 3, '5': 5, '6': 6, '7': 7, '8': 8,
|
|
334
|
-
};
|
|
335
|
-
return map[fontFamily.toLowerCase()];
|
|
336
|
-
}
|
package/src/utils/auth.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import crypto from 'node:crypto';
|
|
2
|
-
import type { Request, Response, NextFunction } from 'express';
|
|
3
|
-
|
|
4
|
-
const SECRET = () => process.env.AUTH_SECRET || process.env.GITHUB_CLIENT_SECRET || 'excalidrop-dev-secret';
|
|
5
|
-
|
|
6
|
-
export interface Session { login: string; id: number; iat: number; }
|
|
7
|
-
|
|
8
|
-
function b64u(buf: Buffer | string): string {
|
|
9
|
-
return Buffer.from(buf as any).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
10
|
-
}
|
|
11
|
-
function unb64u(s: string): Buffer { s = s.replace(/-/g, '+').replace(/_/g, '/'); while (s.length % 4) s += '='; return Buffer.from(s, 'base64'); }
|
|
12
|
-
|
|
13
|
-
export function signSession(s: Session): string {
|
|
14
|
-
const payload = b64u(JSON.stringify(s));
|
|
15
|
-
const sig = b64u(crypto.createHmac('sha256', SECRET()).update(payload).digest());
|
|
16
|
-
return `${payload}.${sig}`;
|
|
17
|
-
}
|
|
18
|
-
export function verifySession(token: string): Session | null {
|
|
19
|
-
try {
|
|
20
|
-
const [p, sig] = token.split('.');
|
|
21
|
-
if (!p || !sig) return null;
|
|
22
|
-
const expect = b64u(crypto.createHmac('sha256', SECRET()).update(p).digest());
|
|
23
|
-
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expect))) return null;
|
|
24
|
-
const s = JSON.parse(unb64u(p).toString()) as Session;
|
|
25
|
-
if (Date.now() / 1000 - s.iat > 30 * 24 * 3600) return null;
|
|
26
|
-
return s;
|
|
27
|
-
} catch { return null; }
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function getToken(req: Request): string | null {
|
|
31
|
-
const h = req.headers.authorization;
|
|
32
|
-
if (h?.startsWith('Bearer ')) return h.slice(7);
|
|
33
|
-
const cookie = req.headers.cookie || '';
|
|
34
|
-
const m = cookie.match(/excalidrop_token=([^;]+)/);
|
|
35
|
-
return m ? decodeURIComponent(m[1] as string) : null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function authOptional(req: Request, _res: Response, next: NextFunction) {
|
|
39
|
-
const t = getToken(req);
|
|
40
|
-
(req as any).user = t ? verifySession(t) : null;
|
|
41
|
-
next();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
export function requireWriteAuth(req: Request, res: Response, next: NextFunction) {
|
|
46
|
-
if (!process.env.GITHUB_CLIENT_ID) return next(); // local dev: open
|
|
47
|
-
const tok = getToken(req);
|
|
48
|
-
const user = tok ? verifySession(tok) : null;
|
|
49
|
-
if (user) { (req as any).user = user; return next(); }
|
|
50
|
-
return res.status(401).json({ success: false, error: 'Authentication required. Login with GitHub.' });
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function requireReadAuth(req: Request, res: Response, next: NextFunction) {
|
|
54
|
-
if (String(process.env.PUBLIC_READ || 'false').toLowerCase() === 'true') { const t = getToken(req); (req as any).user = t ? verifySession(t) : null; return next(); }
|
|
55
|
-
if (!process.env.GITHUB_CLIENT_ID) return next();
|
|
56
|
-
return requireWriteAuth(req, res, next);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function allowedLogins(): string[] {
|
|
60
|
-
return (process.env.ALLOWED_GITHUB_USERS || '').split(',').map(s => s.trim().toLowerCase()).filter(Boolean);
|
|
61
|
-
}
|