diff --git a/package.json b/package.json index 98f70294a0..d7d23a47f5 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "karma-safari-launcher": "^1.0.0", "lint-staged": "^16.4.0", "loading-indicator": "^2.0.0", + "mermaid": "^11.14.0", "pluralize": "^8.0.0", "prettier": "^3.8.3", "prompt": "^1.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a246c49e09..12b2ca3b8f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,6 +135,9 @@ importers: loading-indicator: specifier: ^2.0.0 version: 2.0.0 + mermaid: + specifier: ^11.14.0 + version: 11.14.0 pluralize: specifier: ^8.0.0 version: 8.0.0 @@ -178,6 +181,9 @@ packages: resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} engines: {node: '>=0.10.0'} + '@antfu/install-pkg@1.1.0': + resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} @@ -186,6 +192,24 @@ packages: resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} + '@braintree/sanitize-url@7.1.2': + resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==} + + '@chevrotain/cst-dts-gen@12.0.0': + resolution: {integrity: sha512-fSL4KXjTl7cDgf0B5Rip9Q05BOrYvkJV/RrBTE/bKDN096E4hN/ySpcBK5B24T76dlQ2i32Zc3PAE27jFnFrKg==} + + '@chevrotain/gast@12.0.0': + resolution: {integrity: sha512-1ne/m3XsIT8aEdrvT33so0GUC+wkctpUPK6zU9IlOyJLUbR0rg4G7ZiApiJbggpgPir9ERy3FRjT6T7lpgetnQ==} + + '@chevrotain/regexp-to-ast@12.0.0': + resolution: {integrity: sha512-p+EW9MaJwgaHguhoqwOtx/FwuGr+DnNn857sXWOi/mClXIkPGl3rn7hGNWvo31HA3vyeQxjqe+H36yZJwYU8cA==} + + '@chevrotain/types@12.0.0': + resolution: {integrity: sha512-S+04vjFQKeuYw0/eW3U52LkAHQsB1ASxsPGsLPUyQgrZ2iNNibQrsidruDzjEX2JYfespXMG0eZmXlhA6z7nWA==} + + '@chevrotain/utils@12.0.0': + resolution: {integrity: sha512-lB59uJoaGIfOOL9knQqQRfhl9g7x8/wqFkp13zTdkRu1huG9kg6IJs1O8hqj9rs6h7orGxHJUKb+mX3rPbWGhA==} + '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} @@ -244,6 +268,12 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/utils@3.1.1': + resolution: {integrity: sha512-MwzoDtw9rO1x+qfgLTV/IVXsHDBqeYZoMIQC8SfxfYSlaSUG+oWiAcoiB1yajAda6mqblm4/1/w2E8tRu7a7Tw==} + '@jasminejs/reporters@1.0.0': resolution: {integrity: sha512-rM3GG4vx2H1Gp5kYCTr9aKlOEJFd43pzpiMAiy5b1+FUc2ub4e6bS6yCi/WQNDzAa5MVp9++dwcoEtcIfoEnhA==} @@ -268,6 +298,9 @@ packages: '@jridgewell/trace-mapping@0.3.20': resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} + '@mermaid-js/parser@1.1.0': + resolution: {integrity: sha512-gxK9ZX2+Fex5zu8LhRQoMeMPEHbc73UKZ0FQ54YrQtUxE1VVhMwzeNtKRPAu5aXks4FasbMe4xB4bWrmq6Jlxw==} + '@pkgr/core@0.2.9': resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -469,9 +502,105 @@ packages: '@types/cors@2.8.17': resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + + '@types/d3-axis@3.0.6': + resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} + + '@types/d3-brush@3.0.6': + resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} + + '@types/d3-chord@3.0.6': + resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-contour@3.0.6': + resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} + + '@types/d3-delaunay@6.0.4': + resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} + + '@types/d3-dispatch@3.0.7': + resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==} + + '@types/d3-drag@3.0.7': + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + + '@types/d3-dsv@3.0.7': + resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-fetch@3.0.7': + resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} + + '@types/d3-force@3.0.10': + resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} + + '@types/d3-format@3.0.4': + resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} + + '@types/d3-geo@3.1.0': + resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} + + '@types/d3-hierarchy@3.1.7': + resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-polygon@3.0.2': + resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} + + '@types/d3-quadtree@3.0.6': + resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} + + '@types/d3-random@3.0.3': + resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==} + + '@types/d3-scale-chromatic@3.1.0': + resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-selection@3.0.11': + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + + '@types/d3-shape@3.1.8': + resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} + + '@types/d3-time-format@4.0.3': + resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + + '@types/d3-transition@3.0.9': + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + + '@types/d3-zoom@3.0.8': + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + + '@types/d3@7.4.3': + resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + '@types/html-minifier@3.5.3': resolution: {integrity: sha512-j1P/4PcWVVCPEy5lofcHnQ6BtXz9tHGiFPWzqm7TtGuWZEfCHEP446HlkSNc9fQgNJaJZ6ewPtp2aaFla/Uerg==} @@ -496,6 +625,9 @@ packages: '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/uglify-js@3.17.4': resolution: {integrity: sha512-Hm/T0kV3ywpJyMGNbsItdivRhYNCQQf1IIsYsXnoVPES4t+FMLyDe0/K+Ea7ahWtMtSNb22ZdY7MIyoD9rqARg==} @@ -532,6 +664,9 @@ packages: '@ungap/weakmap@0.2.1': resolution: {integrity: sha512-GmVAWB+JuFKqSbzlofYK4qxk955gEv4Kd9/aj2hLOxneXMAm/J7OXcl5DlElS9tmkqwCcxGysSZGOrjzNvmjFQ==} + '@upsetjs/venn.js@2.0.0': + resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==} + '@zeit/schemas@2.36.0': resolution: {integrity: sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==} @@ -803,6 +938,15 @@ packages: resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + chevrotain-allstar@0.4.3: + resolution: {integrity: sha512-2X4mkroolSMKqW+H22pyPMUVDqYZzPhephTmg/NODKb1IGYPHfxfhcW0EjS7wcPJNbze2i4vBWT7zT5FKF2lrQ==} + peerDependencies: + chevrotain: ^12.0.0 + + chevrotain@12.0.0: + resolution: {integrity: sha512-csJvb+6kEiQaqo1woTdSAuOWdN0WTLIydkKrBnS+V5gZz0oqBrp4kQ35519QgK6TpBThiG3V1vNSHlIkv4AglQ==} + engines: {node: '>=22.0.0'} + chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -882,6 +1026,14 @@ packages: commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} @@ -896,6 +1048,9 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + connect@3.7.0: resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} engines: {node: '>= 0.10.0'} @@ -916,6 +1071,12 @@ packages: resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} engines: {node: '>= 0.10'} + cose-base@1.0.3: + resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} + + cose-base@2.2.0: + resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} + cosmiconfig@9.0.1: resolution: {integrity: sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==} engines: {node: '>=14'} @@ -936,6 +1097,162 @@ packages: resolution: {integrity: sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==} engines: {node: '>=0.4.0'} + cytoscape-cose-bilkent@4.1.0: + resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape-fcose@2.2.0: + resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape@3.33.3: + resolution: {integrity: sha512-Gej7U+OKR+LZ8kvX7rb2HhCYJ0IhvEFsnkud4SB1PR+BUY/TsSO0dmOW59WEVLu51b1Rm+gQRKoz4bLYxGSZ2g==} + engines: {node: '>=0.10'} + + d3-array@2.12.1: + resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-axis@3.0.0: + resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} + engines: {node: '>=12'} + + d3-brush@3.0.0: + resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} + engines: {node: '>=12'} + + d3-chord@3.0.1: + resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-contour@4.0.2: + resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} + engines: {node: '>=12'} + + d3-delaunay@6.0.4: + resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-dsv@3.0.1: + resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} + engines: {node: '>=12'} + hasBin: true + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-fetch@3.0.1: + resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} + engines: {node: '>=12'} + + d3-force@3.0.0: + resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} + engines: {node: '>=12'} + + d3-format@3.1.2: + resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} + engines: {node: '>=12'} + + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + engines: {node: '>=12'} + + d3-hierarchy@3.1.2: + resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@1.0.9: + resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-polygon@3.0.1: + resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} + engines: {node: '>=12'} + + d3-quadtree@3.0.1: + resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} + engines: {node: '>=12'} + + d3-random@3.0.1: + resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} + engines: {node: '>=12'} + + d3-sankey@0.12.3: + resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} + + d3-scale-chromatic@3.1.0: + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-shape@1.3.7: + resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + d3@7.9.0: + resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} + engines: {node: '>=12'} + + dagre-d3-es@7.0.14: + resolution: {integrity: sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==} + data-uri-to-buffer@6.0.2: resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} engines: {node: '>= 14'} @@ -956,6 +1273,9 @@ packages: resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==} engines: {node: '>=4.0'} + dayjs@1.11.20: + resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} + debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -1013,6 +1333,9 @@ packages: resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} engines: {node: '>= 14'} + delaunator@5.1.0: + resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -1043,6 +1366,9 @@ packages: domdiff@2.2.2: resolution: {integrity: sha512-Pv7aH5JfjNIkLRKkah+T3Kd41Gy+7GU+5dmPsOZZXsCJ34QR1i7CCiWraUn1c/a40htShxAQ8mHJR2OqPd12+Q==} + dompurify@3.4.2: + resolution: {integrity: sha512-lHeS9SA/IKeIFFyYciHBr2n0v1VMPlSj843HdLOwjb2OxNwdq9Xykxqhk+FE42MzAdHvInbAolSE4mhahPpjXA==} + domsanitizer@0.2.3: resolution: {integrity: sha512-qglHc+5k5C2+WSEzck0WGbpa2exCKZuZKb0n4RU2Wuy7BPkpmf67mHD1BJWGxs0iJYl709f2YVeJMh06c1ILlA==} @@ -1463,6 +1789,9 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + hachure-fill@0.5.2: + resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} + has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} @@ -1539,6 +1868,10 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + idb@8.0.3: resolution: {integrity: sha512-LtwtVyVYO5BqRvcsKuB2iUMnHwPVByPCXFXOpuU96IZPPoPN6xjOGxZQ74pgSVVLQWtUOYgyeL4GE98BY5D3wg==} @@ -1568,6 +1901,13 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + internmap@1.0.1: + resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + ip-address@10.1.0: resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} engines: {node: '>= 12'} @@ -1796,9 +2136,26 @@ packages: engines: {node: '>= 10'} hasBin: true + katex@0.16.45: + resolution: {integrity: sha512-pQpZbdBu7wCTmQUh7ufPmLr0pFoObnGUoL/yhtwJDgmmQpbkg/0HSVti25Fu4rmd1oCR6NGWe9vqTWuWv3GcNA==} + hasBin: true + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + khroma@2.1.0: + resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} + + langium@4.2.3: + resolution: {integrity: sha512-sOPIi4hISFnY7twwV97ca1TsxpBtXq0URu/LL1AvxwccPG/RIBBlKS7a/f/EL6w8lTNaS0EFs/F+IdSOaqYpng==} + engines: {node: '>=20.10.0', npm: '>=10.2.3'} + + layout-base@1.0.2: + resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} + + layout-base@2.0.1: + resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -1823,6 +2180,9 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash-es@4.18.1: + resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -1863,6 +2223,11 @@ packages: resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} engines: {node: '>=12'} + marked@16.4.2: + resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==} + engines: {node: '>= 20'} + hasBin: true + marked@18.0.2: resolution: {integrity: sha512-NsmlUYBS/Zg57rgDWMYdnre6OTj4e+qq/JS2ot3KrYLSoHLw+sDu0Nm1ZGpRgYAq6c+b1ekaY5NzVchMCQnzcg==} engines: {node: '>= 20'} @@ -1879,6 +2244,9 @@ packages: merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + mermaid@11.14.0: + resolution: {integrity: sha512-GSGloRsBs+JINmmhl0JDwjpuezCsHB4WGI4NASHxL3fHo3o/BRXTxhDLKnln8/Q0lRFRyDdEjmk1/d5Sn1Xz8g==} + mime-db@1.33.0: resolution: {integrity: sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==} engines: {node: '>= 0.6'} @@ -1940,6 +2308,9 @@ packages: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -2058,6 +2429,9 @@ packages: resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} engines: {node: '>= 14'} + package-manager-detector@1.6.0: + resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} + param-case@2.1.1: resolution: {integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==} @@ -2076,6 +2450,9 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} + path-data-parser@0.1.0: + resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -2101,6 +2478,9 @@ packages: path-to-regexp@3.3.0: resolution: {integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} @@ -2115,10 +2495,19 @@ packages: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} + points-on-curve@0.2.0: + resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} + + points-on-path@0.2.1: + resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} + possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -2264,6 +2653,9 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true + robust-predicates@3.0.3: + resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} + rollup-plugin-minify-html-literals@1.2.6: resolution: {integrity: sha512-JRq2fjlCTiw0zu+1Sy3ClHGCxA79dWGr4HLHWSQgd060StVW9fBVksuj8Xw/suPkNSGClJf/4xNQ1MF6JeXPaw==} peerDependencies: @@ -2277,6 +2669,12 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + roughjs@4.6.6: + resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} + + rw@1.3.3: + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + sade@1.8.1: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} engines: {node: '>=6'} @@ -2512,6 +2910,9 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + stylis@4.4.0: + resolution: {integrity: sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==} + supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -2561,6 +2962,10 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + ts-dedent@2.2.0: + resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} + engines: {node: '>=6.10'} + tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} @@ -2618,6 +3023,9 @@ packages: uarray@1.0.0: resolution: {integrity: sha512-LHmiAd5QuAv7pU2vbh+Zq9YOnqVK0H764p2Ozinpfy9ka58OID4IsGLiXsitqH7n0NAIDxvax1A/kDXpii/Ckg==} + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + uglify-js@3.17.4: resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} engines: {node: '>=0.8.0'} @@ -2657,6 +3065,10 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} + uuid@11.1.1: + resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==} + hasBin: true + vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -2670,6 +3082,26 @@ packages: resolution: {integrity: sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==} engines: {node: '>=0.10.0'} + vscode-jsonrpc@8.2.0: + resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} + engines: {node: '>=14.0.0'} + + vscode-languageserver-protocol@3.17.5: + resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} + + vscode-languageserver-textdocument@1.0.12: + resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} + + vscode-languageserver-types@3.17.5: + resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} + + vscode-languageserver@9.0.1: + resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} + hasBin: true + + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + webdriver-bidi-protocol@0.4.1: resolution: {integrity: sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==} @@ -2785,6 +3217,11 @@ snapshots: '@aashutoshrathi/word-wrap@1.2.6': {} + '@antfu/install-pkg@1.1.0': + dependencies: + package-manager-detector: 1.6.0 + tinyexec: 1.1.1 + '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -2793,6 +3230,23 @@ snapshots: '@babel/helper-validator-identifier@7.28.5': {} + '@braintree/sanitize-url@7.1.2': {} + + '@chevrotain/cst-dts-gen@12.0.0': + dependencies: + '@chevrotain/gast': 12.0.0 + '@chevrotain/types': 12.0.0 + + '@chevrotain/gast@12.0.0': + dependencies: + '@chevrotain/types': 12.0.0 + + '@chevrotain/regexp-to-ast@12.0.0': {} + + '@chevrotain/types@12.0.0': {} + + '@chevrotain/utils@12.0.0': {} + '@colors/colors@1.5.0': {} '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4)': @@ -2852,6 +3306,14 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@iconify/types@2.0.0': {} + + '@iconify/utils@3.1.1': + dependencies: + '@antfu/install-pkg': 1.1.0 + '@iconify/types': 2.0.0 + mlly: 1.8.2 + '@jasminejs/reporters@1.0.0': {} '@jridgewell/gen-mapping@0.3.3': @@ -2876,6 +3338,10 @@ snapshots: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 + '@mermaid-js/parser@1.1.0': + dependencies: + langium: 4.2.3 + '@pkgr/core@0.2.9': {} '@puppeteer/browsers@2.13.0': @@ -3023,8 +3489,127 @@ snapshots: dependencies: '@types/node': 24.2.0 + '@types/d3-array@3.2.2': {} + + '@types/d3-axis@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-brush@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-chord@3.0.6': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-contour@3.0.6': + dependencies: + '@types/d3-array': 3.2.2 + '@types/geojson': 7946.0.16 + + '@types/d3-delaunay@6.0.4': {} + + '@types/d3-dispatch@3.0.7': {} + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-dsv@3.0.7': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-fetch@3.0.7': + dependencies: + '@types/d3-dsv': 3.0.7 + + '@types/d3-force@3.0.10': {} + + '@types/d3-format@3.0.4': {} + + '@types/d3-geo@3.1.0': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/d3-hierarchy@3.1.7': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-polygon@3.0.2': {} + + '@types/d3-quadtree@3.0.6': {} + + '@types/d3-random@3.0.3': {} + + '@types/d3-scale-chromatic@3.1.0': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-selection@3.0.11': {} + + '@types/d3-shape@3.1.8': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time-format@4.0.3': {} + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + + '@types/d3@7.4.3': + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-axis': 3.0.6 + '@types/d3-brush': 3.0.6 + '@types/d3-chord': 3.0.6 + '@types/d3-color': 3.1.3 + '@types/d3-contour': 3.0.6 + '@types/d3-delaunay': 6.0.4 + '@types/d3-dispatch': 3.0.7 + '@types/d3-drag': 3.0.7 + '@types/d3-dsv': 3.0.7 + '@types/d3-ease': 3.0.2 + '@types/d3-fetch': 3.0.7 + '@types/d3-force': 3.0.10 + '@types/d3-format': 3.0.4 + '@types/d3-geo': 3.1.0 + '@types/d3-hierarchy': 3.1.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-path': 3.1.1 + '@types/d3-polygon': 3.0.2 + '@types/d3-quadtree': 3.0.6 + '@types/d3-random': 3.0.3 + '@types/d3-scale': 4.0.9 + '@types/d3-scale-chromatic': 3.1.0 + '@types/d3-selection': 3.0.11 + '@types/d3-shape': 3.1.8 + '@types/d3-time': 3.0.4 + '@types/d3-time-format': 4.0.3 + '@types/d3-timer': 3.0.2 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + '@types/estree@1.0.8': {} + '@types/geojson@7946.0.16': {} + '@types/html-minifier@3.5.3': dependencies: '@types/clean-css': 4.2.11 @@ -3050,6 +3635,9 @@ snapshots: '@types/resolve@1.20.2': {} + '@types/trusted-types@2.0.7': + optional: true + '@types/uglify-js@3.17.4': dependencies: source-map: 0.6.1 @@ -3083,6 +3671,11 @@ snapshots: '@ungap/weakmap@0.2.1': {} + '@upsetjs/venn.js@2.0.0': + optionalDependencies: + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + '@zeit/schemas@2.36.0': {} accepts@1.3.8: @@ -3369,6 +3962,19 @@ snapshots: chalk@5.3.0: {} + chevrotain-allstar@0.4.3(chevrotain@12.0.0): + dependencies: + chevrotain: 12.0.0 + lodash-es: 4.18.1 + + chevrotain@12.0.0: + dependencies: + '@chevrotain/cst-dts-gen': 12.0.0 + '@chevrotain/gast': 12.0.0 + '@chevrotain/regexp-to-ast': 12.0.0 + '@chevrotain/types': 12.0.0 + '@chevrotain/utils': 12.0.0 + chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -3454,6 +4060,10 @@ snapshots: commander@2.20.3: {} + commander@7.2.0: {} + + commander@8.3.0: {} + commondir@1.0.1: {} compressible@2.0.18: @@ -3474,6 +4084,8 @@ snapshots: concat-map@0.0.1: {} + confbox@0.1.8: {} + connect@3.7.0: dependencies: debug: 2.6.9 @@ -3494,6 +4106,14 @@ snapshots: object-assign: 4.1.1 vary: 1.1.2 + cose-base@1.0.3: + dependencies: + layout-base: 1.0.2 + + cose-base@2.2.0: + dependencies: + layout-base: 2.0.1 + cosmiconfig@9.0.1(typescript@6.0.3): dependencies: env-paths: 2.2.1 @@ -3513,6 +4133,190 @@ snapshots: cycle@1.0.3: {} + cytoscape-cose-bilkent@4.1.0(cytoscape@3.33.3): + dependencies: + cose-base: 1.0.3 + cytoscape: 3.33.3 + + cytoscape-fcose@2.2.0(cytoscape@3.33.3): + dependencies: + cose-base: 2.2.0 + cytoscape: 3.33.3 + + cytoscape@3.33.3: {} + + d3-array@2.12.1: + dependencies: + internmap: 1.0.1 + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-axis@3.0.0: {} + + d3-brush@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3-chord@3.0.1: + dependencies: + d3-path: 3.1.0 + + d3-color@3.1.0: {} + + d3-contour@4.0.2: + dependencies: + d3-array: 3.2.4 + + d3-delaunay@6.0.4: + dependencies: + delaunator: 5.1.0 + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-dsv@3.0.1: + dependencies: + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 + + d3-ease@3.0.1: {} + + d3-fetch@3.0.1: + dependencies: + d3-dsv: 3.0.1 + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-format@3.1.2: {} + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d3-hierarchy@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@1.0.9: {} + + d3-path@3.1.0: {} + + d3-polygon@3.0.1: {} + + d3-quadtree@3.0.1: {} + + d3-random@3.0.1: {} + + d3-sankey@0.12.3: + dependencies: + d3-array: 2.12.1 + d3-shape: 1.3.7 + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.2 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-selection@3.0.0: {} + + d3-shape@1.3.7: + dependencies: + d3-path: 1.0.9 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3@7.9.0: + dependencies: + d3-array: 3.2.4 + d3-axis: 3.0.0 + d3-brush: 3.0.0 + d3-chord: 3.0.1 + d3-color: 3.1.0 + d3-contour: 4.0.2 + d3-delaunay: 6.0.4 + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-dsv: 3.0.1 + d3-ease: 3.0.1 + d3-fetch: 3.0.1 + d3-force: 3.0.0 + d3-format: 3.1.2 + d3-geo: 3.1.1 + d3-hierarchy: 3.1.2 + d3-interpolate: 3.0.1 + d3-path: 3.1.0 + d3-polygon: 3.0.1 + d3-quadtree: 3.0.1 + d3-random: 3.0.1 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-selection: 3.0.0 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + d3-timer: 3.0.1 + d3-transition: 3.0.1(d3-selection@3.0.0) + d3-zoom: 3.0.0 + + dagre-d3-es@7.0.14: + dependencies: + d3: 7.9.0 + lodash-es: 4.18.1 + data-uri-to-buffer@6.0.2: {} data-view-buffer@1.0.2: @@ -3535,6 +4339,8 @@ snapshots: date-format@4.0.14: {} + dayjs@1.11.20: {} + debug@2.6.9: dependencies: ms: 2.0.0 @@ -3575,6 +4381,10 @@ snapshots: escodegen: 2.1.0 esprima: 4.0.1 + delaunator@5.1.0: + dependencies: + robust-predicates: 3.0.3 + depd@2.0.0: {} destroy@1.2.0: {} @@ -3602,6 +4412,10 @@ snapshots: dependencies: uarray: 1.0.0 + dompurify@3.4.2: + optionalDependencies: + '@types/trusted-types': 2.0.7 + domsanitizer@0.2.3: dependencies: domconstants: 0.1.2 @@ -4115,6 +4929,8 @@ snapshots: graceful-fs@4.2.11: {} + hachure-fill@0.5.2: {} + has-bigints@1.0.2: {} has-flag@3.0.0: {} @@ -4208,6 +5024,10 @@ snapshots: dependencies: safer-buffer: 2.1.2 + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + idb@8.0.3: {} ignore@5.3.0: {} @@ -4234,6 +5054,10 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 + internmap@1.0.1: {} + + internmap@2.0.3: {} + ip-address@10.1.0: {} is-array-buffer@3.0.5: @@ -4469,10 +5293,29 @@ snapshots: - supports-color - utf-8-validate + katex@0.16.45: + dependencies: + commander: 8.3.0 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 + khroma@2.1.0: {} + + langium@4.2.3: + dependencies: + '@chevrotain/regexp-to-ast': 12.0.0 + chevrotain: 12.0.0 + chevrotain-allstar: 0.4.3(chevrotain@12.0.0) + vscode-languageserver: 9.0.1 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + + layout-base@1.0.2: {} + + layout-base@2.0.1: {} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -4506,6 +5349,8 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash-es@4.18.1: {} + lodash.merge@4.6.2: {} lodash@4.18.1: {} @@ -4551,6 +5396,8 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 + marked@16.4.2: {} + marked@18.0.2: {} math-intrinsics@1.1.0: {} @@ -4559,6 +5406,30 @@ snapshots: merge-stream@2.0.0: {} + mermaid@11.14.0: + dependencies: + '@braintree/sanitize-url': 7.1.2 + '@iconify/utils': 3.1.1 + '@mermaid-js/parser': 1.1.0 + '@types/d3': 7.4.3 + '@upsetjs/venn.js': 2.0.0 + cytoscape: 3.33.3 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.33.3) + cytoscape-fcose: 2.2.0(cytoscape@3.33.3) + d3: 7.9.0 + d3-sankey: 0.12.3 + dagre-d3-es: 7.0.14 + dayjs: 1.11.20 + dompurify: 3.4.2 + katex: 0.16.45 + khroma: 2.1.0 + lodash-es: 4.18.1 + marked: 16.4.2 + roughjs: 4.6.6 + stylis: 4.4.0 + ts-dedent: 2.2.0 + uuid: 11.1.1 + mime-db@1.33.0: {} mime-db@1.52.0: {} @@ -4609,6 +5480,13 @@ snapshots: dependencies: minimist: 1.2.8 + mlly@1.8.2: + dependencies: + acorn: 8.16.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.4 + mri@1.2.0: {} ms@2.0.0: {} @@ -4735,6 +5613,8 @@ snapshots: degenerator: 5.0.1 netmask: 2.1.1 + package-manager-detector@1.6.0: {} + param-case@2.1.1: dependencies: no-case: 2.3.2 @@ -4756,6 +5636,8 @@ snapshots: parseurl@1.3.3: {} + path-data-parser@0.1.0: {} + path-exists@4.0.0: {} path-is-absolute@1.0.1: {} @@ -4773,6 +5655,8 @@ snapshots: path-to-regexp@3.3.0: {} + pathe@2.0.3: {} + pend@1.2.0: {} picocolors@1.1.1: {} @@ -4781,8 +5665,21 @@ snapshots: picomatch@4.0.4: {} + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 + pluralize@8.0.0: {} + points-on-curve@0.2.0: {} + + points-on-path@0.2.1: + dependencies: + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + possible-typed-array-names@1.0.0: {} prelude-ls@1.2.1: {} @@ -4958,6 +5855,8 @@ snapshots: dependencies: glob: 7.2.3 + robust-predicates@3.0.3: {} + rollup-plugin-minify-html-literals@1.2.6(rollup@4.60.2): dependencies: minify-html-literals: 1.3.5 @@ -4999,6 +5898,15 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.60.2 fsevents: 2.3.3 + roughjs@4.6.6: + dependencies: + hachure-fill: 0.5.2 + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + points-on-path: 0.2.1 + + rw@1.3.3: {} + sade@1.8.1: dependencies: mri: 1.2.0 @@ -5313,6 +6221,8 @@ snapshots: strip-json-comments@3.1.1: {} + stylis@4.4.0: {} + supports-color@5.5.0: dependencies: has-flag: 3.0.0 @@ -5380,6 +6290,8 @@ snapshots: toidentifier@1.0.1: {} + ts-dedent@2.2.0: {} + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 @@ -5445,6 +6357,8 @@ snapshots: uarray@1.0.0: {} + ufo@1.6.4: {} + uglify-js@3.17.4: {} umap@1.0.2: {} @@ -5478,12 +6392,31 @@ snapshots: utils-merge@1.0.1: {} + uuid@11.1.1: {} + vary@1.1.2: {} vnu-jar@26.4.16: {} void-elements@2.0.1: {} + vscode-jsonrpc@8.2.0: {} + + vscode-languageserver-protocol@3.17.5: + dependencies: + vscode-jsonrpc: 8.2.0 + vscode-languageserver-types: 3.17.5 + + vscode-languageserver-textdocument@1.0.12: {} + + vscode-languageserver-types@3.17.5: {} + + vscode-languageserver@9.0.1: + dependencies: + vscode-languageserver-protocol: 3.17.5 + + vscode-uri@3.1.0: {} + webdriver-bidi-protocol@0.4.1: {} webidl2@24.5.0: {} diff --git a/profiles/w3c.js b/profiles/w3c.js index cdf270af71..f5e1709312 100644 --- a/profiles/w3c.js +++ b/profiles/w3c.js @@ -27,6 +27,7 @@ const modules = [ import("../src/core/issues-notes.js"), import("../src/core/best-practices.js"), import("../src/core/figures.js"), + import("../src/core/diagrams.js"), import("../src/core/tables.js"), import("../src/core/webidl.js"), import("../src/core/cddl.js"), diff --git a/src/core/clipboard.js b/src/core/clipboard.js index cc9317b8c2..d075921236 100644 --- a/src/core/clipboard.js +++ b/src/core/clipboard.js @@ -9,6 +9,24 @@ const COPY_SVG = ''; +/** + * Find the copy target
 for a button.
+ * If the button has a data-copy-container attribute, searches within that
+ * ancestor. Otherwise falls back to closest("pre").
+ * @param {HTMLElement} button
+ * @returns {HTMLElement | null}
+ */
+function findCopyTarget(button) {
+  const containerSel = button.dataset.copyContainer;
+  if (containerSel) {
+    const container = button.closest(containerSel);
+    return /** @type {HTMLElement | null} */ (
+      container?.querySelector("pre") ?? null
+    );
+  }
+  return /** @type {HTMLElement | null} */ (button.closest("pre"));
+}
+
 /**
  * Create a copy-to-clipboard button for a code block.
  * The button stores the header selector in a data attribute so the
@@ -16,17 +34,36 @@ const COPY_SVG =
  *
  * @param {string} headerSelector - Selector for the header to exclude from copy
  * @param {string} [title="Copy to clipboard"] - Accessible label and tooltip
+ * @param {string} [containerSelector] - Ancestor selector to find the 
 within
  * @returns {HTMLButtonElement}
  */
-export function createCopyButton(headerSelector, title = "Copy to clipboard") {
+export function createCopyButton(
+  headerSelector,
+  title = "Copy to clipboard",
+  containerSelector
+) {
   const button = document.createElement("button");
   button.innerHTML = COPY_SVG;
   button.title = title;
   button.setAttribute("aria-label", title);
   button.classList.add("respec-button-copy-paste", "removeOnSave");
   button.dataset.copyHeader = headerSelector;
+  if (containerSelector) {
+    button.dataset.copyContainer = containerSelector;
+  }
   button.addEventListener("click", () => {
-    const pre = button.closest("pre");
+    const containerSel = button.dataset.copyContainer;
+    if (containerSel) {
+      const container = /** @type {HTMLElement | null} */ (
+        button.closest(containerSel)
+      );
+      const source = container?.dataset.diagramSource;
+      if (source) {
+        navigator.clipboard.writeText(source);
+        return;
+      }
+    }
+    const pre = findCopyTarget(button);
     if (!pre) return;
     const clone = /** @type {HTMLElement} */ (pre.cloneNode(true));
     clone.querySelector(headerSelector)?.remove();
@@ -46,7 +83,18 @@ export function injectCopyScript() {
   script.textContent = `
     document.querySelectorAll(".respec-button-copy-paste").forEach(function(btn) {
       btn.addEventListener("click", function() {
-        var pre = this.closest("pre");
+        var containerSel = this.dataset.copyContainer;
+        var pre = null;
+        if (containerSel) {
+          var container = this.closest(containerSel);
+          if (container && container.dataset.diagramSource) {
+            navigator.clipboard.writeText(container.dataset.diagramSource);
+            return;
+          }
+          pre = container ? container.querySelector("pre") : null;
+        } else {
+          pre = this.closest("pre");
+        }
         if (!pre) return;
         var sel = this.dataset.copyHeader;
         var clone = pre.cloneNode(true);
diff --git a/src/core/diagrams-runtime.js b/src/core/diagrams-runtime.js
new file mode 100644
index 0000000000..c14a867a68
--- /dev/null
+++ b/src/core/diagrams-runtime.js
@@ -0,0 +1,33 @@
+// @ts-check
+/**
+ * Module core/diagrams-runtime
+ *
+ * Runtime script injected into published specs.
+ * Handles flip buttons: toggles the flipped class for 3D rotation,
+ * updates ARIA state, and manages back-face height.
+ */
+
+export const runtimeScript = `
+  "use strict";
+  (function() {
+    document.querySelectorAll(".diagram-flip-btn").forEach(function(btn) {
+      btn.addEventListener("click", function(e) {
+        e.stopPropagation();
+        var container = btn.closest(".diagram-container");
+        if (!container) return;
+        var isFlipped = container.classList.toggle("diagram-container--flipped");
+        btn.setAttribute("aria-expanded", String(isFlipped));
+        var viewSource = btn.dataset.labelSource;
+        var viewDiagram = btn.dataset.labelDiagram;
+        if (viewSource && viewDiagram) {
+          btn.setAttribute("aria-label", isFlipped ? viewDiagram : viewSource);
+        }
+        var flip = container.querySelector(".diagram-flip");
+        var back = container.querySelector(".diagram-face--back");
+        if (flip && back) {
+          flip.style.minHeight = isFlipped ? back.scrollHeight + "px" : "";
+        }
+      });
+    });
+  })();
+`;
diff --git a/src/core/diagrams.js b/src/core/diagrams.js
new file mode 100644
index 0000000000..d069c7f924
--- /dev/null
+++ b/src/core/diagrams.js
@@ -0,0 +1,348 @@
+// @ts-check
+/**
+ * Module core/diagrams
+ *
+ * Coordinator for diagram rendering. Detects diagram blocks,
+ * lazy-loads the appropriate renderer, builds the flip-card DOM,
+ * handles errors, and injects the runtime script.
+ */
+import { createCopyButton, injectCopyScript } from "./clipboard.js";
+import { getIntlData, showError, showWarning } from "./utils.js";
+import css from "../styles/diagrams.css.js";
+import { html } from "./import-maps.js";
+import { runtimeScript } from "./diagrams-runtime.js";
+
+export const name = "core/diagrams";
+
+const localizationStrings = {
+  en: {
+    diagram_error: "Diagram error!",
+    click_to_view_source: "Diagram. Click to view source.",
+    click_to_hide_source: "Source. Click to view diagram.",
+    copy_aria: "Copy diagram source",
+    load_failed: "Could not load diagram renderer. Diagrams shown as source.",
+  },
+  ja: {
+    diagram_error: "図のエラー:",
+    click_to_view_source: "図。クリックでソースを表示。",
+    click_to_hide_source: "ソース。クリックで図を表示。",
+    copy_aria: "図のソースをコピー",
+    load_failed: "図レンダラーを読み込めませんでした。",
+  },
+  ko: {
+    diagram_error: "다이어그램 오류:",
+    click_to_view_source: "다이어그램. 클릭하여 소스 보기.",
+    click_to_hide_source: "소스. 클릭하여 다이어그램 보기.",
+    copy_aria: "다이어그램 소스 복사",
+    load_failed: "다이어그램 렌더러를 로드할 수 없습니다.",
+  },
+  nl: {
+    diagram_error: "Diagramfout:",
+    click_to_view_source: "Diagram. Klik om bron te bekijken.",
+    click_to_hide_source: "Bron. Klik om diagram te bekijken.",
+    copy_aria: "Diagrambron kopiëren",
+    load_failed: "Kon diagramrenderer niet laden.",
+  },
+  es: {
+    diagram_error: "Error de diagrama:",
+    click_to_view_source: "Diagrama. Haga clic para ver el código fuente.",
+    click_to_hide_source: "Código fuente. Haga clic para ver el diagrama.",
+    copy_aria: "Copiar código del diagrama",
+    load_failed: "No se pudo cargar el renderizador de diagramas.",
+  },
+  fr: {
+    diagram_error: "Erreur de diagramme :",
+    click_to_view_source: "Diagramme. Cliquez pour voir le source.",
+    click_to_hide_source: "Source. Cliquez pour voir le diagramme.",
+    copy_aria: "Copier le source du diagramme",
+    load_failed: "Impossible de charger le moteur de diagrammes.",
+  },
+};
+const l10n = getIntlData(localizationStrings);
+
+const PRODUCTION_MERMAID_URL = "https://www.w3.org/Tools/respec/respec-mermaid";
+
+/**
+ * Lazy-load the mermaid library via script injection.
+ * @returns {Promise} true if loaded successfully
+ */
+async function loadMermaidLib() {
+  // @ts-ignore
+  if (self.respecMermaid) return true;
+
+  const primaryUrl = new URL("respec-mermaid.js", import.meta.url).href;
+
+  for (const url of [primaryUrl, PRODUCTION_MERMAID_URL]) {
+    try {
+      await loadScript(url);
+      // @ts-ignore
+      if (self.respecMermaid) return true;
+    } catch {
+      // fallback to next URL
+    }
+  }
+  return false;
+}
+
+/**
+ * @param {string} url
+ * @returns {Promise}
+ */
+function loadScript(url) {
+  return new Promise((resolve, reject) => {
+    const script = document.createElement("script");
+    script.src = url;
+    script.onload = () => {
+      script.remove();
+      resolve();
+    };
+    script.onerror = event => {
+      script.remove();
+      reject(new Error(`Failed to load: ${url}`, { cause: event }));
+    };
+    document.head.append(script);
+  });
+}
+
+/**
+ * Build the diagram DOM with classic 3D flip card pattern.
+ * @param {string} svg
+ * @param {string} source
+ * @returns {HTMLElement}
+ */
+function buildDiagramBlock(svg, source) {
+  const copyBtn = createCopyButton(
+    ".diagramHeader",
+    l10n.copy_aria,
+    ".diagram-container"
+  );
+  return html`
+
+ Mermaid + + ${copyBtn} +
+
+
${{ html: svg }}
+
+
${source}
+
+
+
`; +} + +/** + * Parse mermaid error to extract line, pointer, and description. + * @param {string} errorMsg + * @returns {{ line: number | null, pointer: string, description: string }} + */ +function parseMermaidError(errorMsg) { + const lineMatch = errorMsg.match(/line (\d+)/i); + const line = lineMatch ? parseInt(lineMatch[1], 10) : null; + const parts = errorMsg.split("\n"); + let pointer = ""; + let description = ""; + const pointerIndex = parts.findIndex( + p => p.includes("^") && !p.startsWith("Parse") + ); + if (pointerIndex !== -1) { + pointer = parts[pointerIndex]; + description = parts + .slice(pointerIndex + 1) + .filter(Boolean) + .join(" ") + .trim(); + } + if (!description) description = [...parts].reverse().find(Boolean) || ""; + return { line, pointer, description }; +} + +/** + * Build source with line numbers and inline error pointer. + * @param {string} source + * @param {{ line: number | null, pointer: string, description: string }} error + * @returns {HTMLElement} + */ +function buildNumberedSource(source, error) { + const code = document.createElement("code"); + code.className = "diagram-source-grid nohighlight"; + const lines = source.split("\n"); + lines.forEach((line, i) => { + const lineNum = i + 1; + const isError = lineNum === error.line; + + const indicator = document.createElement("span"); + indicator.className = isError + ? "dg-indicator dg-indicator--error" + : "dg-indicator"; + if (isError) { + indicator.textContent = "⚠️"; + if (error.description) { + indicator.style.gridRow = "span 2"; + } + } + + const gutter = document.createElement("span"); + gutter.className = isError ? "dg-gutter dg-gutter--error" : "dg-gutter"; + gutter.textContent = String(lineNum); + + const content = document.createElement("span"); + content.className = isError ? "dg-code dg-code--error" : "dg-code"; + content.textContent = line; + + code.append(indicator, gutter, content); + + if (lineNum === error.line && error.description) { + const hintGutter = document.createElement("span"); + hintGutter.className = "dg-gutter dg-gutter--error"; + const hintCode = document.createElement("span"); + hintCode.className = "dg-code dg-code--hint"; + hintCode.textContent = error.pointer + ? `${error.pointer} ${error.description}` + : `^ ${error.description}`; + code.append(hintGutter, hintCode); + } + }); + return code; +} + +/** + * Build the error DOM with 3D flip card. + * Front: "Diagram error!" message. + * Back: line-numbered source with inline error pointer. + * @param {string} errorMsg + * @param {string} source + * @returns {HTMLElement} + */ +function buildErrorBlock(errorMsg, source) { + const error = parseMermaidError(errorMsg); + const numberedSource = buildNumberedSource(source, error); + const copyBtn = createCopyButton( + ".diagramHeader", + l10n.copy_aria, + ".diagram-container" + ); + return html`
+
+ Mermaid + + ${copyBtn} +
+
+
+ ${l10n.diagram_error} +
+
+
${numberedSource}
+
+
+
`; +} + +/** + * @param {Conf} conf + */ +export async function run(conf) { + /** @type {NodeListOf} */ + const mermaidBlocks = document.querySelectorAll("pre.mermaid"); + + if (!mermaidBlocks.length) return; + + const uncaptioned = [...mermaidBlocks].filter(pre => { + const figure = pre.closest("figure"); + return !figure || !figure.querySelector("figcaption"); + }); + if (uncaptioned.length) { + showWarning( + "Diagram blocks must be wrapped in a `
` with a `
`.", + name, + { + hint: "Wrap the `
` in a `
` element and add a `
`.", + elements: uncaptioned, + } + ); + } + + let counter = 0; + + const style = document.createElement("style"); + style.textContent = css; + document.head.append(style); + + const loaded = await loadMermaidLib(); + if (!loaded) { + showWarning(l10n.load_failed, name); + return; + } + + // @ts-expect-error -- mermaid config not in Conf type yet + const theme = conf.mermaid?.theme || "neutral"; + // @ts-ignore + self.respecMermaid.initialize({ theme }); + + const mermaidRenderer = await import("./diagrams/mermaid.js"); + + const renderPromises = [...mermaidBlocks].map(async pre => { + const source = pre.textContent?.trim(); + if (!source) return; + + const figure = pre.closest("figure"); + + const figcaption = figure?.querySelector("figcaption") ?? null; + + if (figcaption && !figcaption.id) { + const figureId = figure?.id || ""; + figcaption.id = figureId + ? `${figureId}-caption` + : `respec-diagram-caption-${counter}`; + counter++; + } + + const result = await mermaidRenderer.render(source); + + if ("svg" in result) { + const diagramBlock = buildDiagramBlock(result.svg, source); + pre.replaceWith(diagramBlock); + } else { + const errorBlock = buildErrorBlock(result.error, source); + pre.replaceWith(errorBlock); + showError( + `Mermaid diagram parse error${figure?.id ? ` in figure '${figure.id}'` : ""}: ${result.error}`, + name, + { elements: figure ? [/** @type {HTMLElement} */ (figure)] : [] } + ); + } + }); + + await Promise.allSettled(renderPromises); + + injectCopyScript(); + + if (!document.getElementById("respec-diagrams-runtime")) { + const script = document.createElement("script"); + script.id = "respec-diagrams-runtime"; + script.textContent = runtimeScript; + document.body.append(script); + } +} diff --git a/src/core/diagrams/mermaid.js b/src/core/diagrams/mermaid.js new file mode 100644 index 0000000000..c18f6a7a90 --- /dev/null +++ b/src/core/diagrams/mermaid.js @@ -0,0 +1,26 @@ +// @ts-check +/** + * Module core/diagrams/mermaid + * + * Pure renderer for Mermaid diagrams. Delegates to the globally-loaded + * mermaid library (loaded by the coordinator via script injection). + */ +export const name = "core/diagrams/mermaid"; + +export const className = "mermaid"; + +let counter = 0; + +/** + * @param {string} source + * @returns {Promise<{ svg: string } | { error: string }>} + */ +export async function render(source) { + // @ts-ignore -- loaded dynamically via script injection + if (!self.respecMermaid) { + return { error: "Mermaid library not loaded." }; + } + const id = `respec-mermaid-diagram-${counter++}`; + // @ts-ignore + return self.respecMermaid.render(source, { id }); +} diff --git a/src/core/highlight.js b/src/core/highlight.js index 5a07aafe87..16b8881852 100644 --- a/src/core/highlight.js +++ b/src/core/highlight.js @@ -91,8 +91,8 @@ export async function run(conf) { const highlightables = [ .../** @type {NodeListOf} */ ( document.querySelectorAll(` - pre:not(.idl):not(.cddl):not(.nohighlight) > code:not(.nohighlight), - pre:not(.idl):not(.cddl):not(.nohighlight), + pre:not(.idl):not(.cddl):not(.mermaid):not(.nohighlight) > code:not(.nohighlight), + pre:not(.idl):not(.cddl):not(.mermaid):not(.nohighlight), code.highlight `) ), diff --git a/src/styles/diagrams.css.js b/src/styles/diagrams.css.js new file mode 100644 index 0000000000..1e1cc84902 --- /dev/null +++ b/src/styles/diagrams.css.js @@ -0,0 +1,329 @@ +/* --- Diagrams --- */ +const css = String.raw; + +// prettier-ignore +export default css` +:root { + --diagram-error-border: #d73a49; + --diagram-error-bg: #ffeef0; + --diagram-error-color: #86181d; + --diagram-header-bg: #005a9c; + --diagram-header-color: #fff; + --diagram-back-bg: #f6f8fa; + --diagram-btn-bg: #f0f0f0; + --diagram-btn-border: #ccc; + --diagram-btn-color: #555; + --diagram-btn-hover-bg: #e0e0e0; + --diagram-btn-hover-color: #333; + --diagram-pulse-from: #f0f0f0; + --diagram-pulse-to: #f0b8bb; +} + +@media (prefers-color-scheme: dark) { + :root { + --diagram-error-border: #f85149; + --diagram-error-bg: #3d1f28; + --diagram-error-color: #ff7b72; + --diagram-header-bg: #3a6da0; + --diagram-header-color: #fff; + --diagram-back-bg: #1e1e1e; + --diagram-btn-bg: #2d2d2d; + --diagram-btn-border: #555; + --diagram-btn-color: #aaa; + --diagram-btn-hover-bg: #3d3d3d; + --diagram-btn-hover-color: #ddd; + --diagram-pulse-from: #2d2d2d; + --diagram-pulse-to: #5a2d31; + } +} + +.diagram-container { + border: 2px solid transparent; + transition: border-color 0.3s; + min-width: min(20em, 100%); + perspective: 1000px; +} + +.diagram-container:hover, +.diagram-container:focus-within, +.diagram-container--flipped { + border-color: var(--diagram-header-bg); +} + +.diagram-flip { + position: relative; + width: 100%; + transform-style: preserve-3d; + transition: transform 0.5s ease-in-out; +} + +.diagram-container--flipped .diagram-flip { + transform: rotateY(180deg); +} + +.diagram-face { + position: absolute; + inset: 0; + backface-visibility: hidden; +} + +.diagram-face--front { + position: relative; +} + +.diagram-face--front svg { + display: block; + max-width: 100%; + height: auto; +} + +.diagram-face--back { + transform: rotateY(180deg); + overflow: auto; + background: var(--diagram-back-bg); +} + +.diagram-face--back pre { + margin: 0; + padding: 1em; + width: 100%; + height: 100%; + box-sizing: border-box; + overflow: auto; +} + +.diagram-face--back code { + white-space: pre; +} + +.diagramHeader { + display: flex; + align-items: stretch; + opacity: 0; + visibility: hidden; + transition: opacity 0.3s, visibility 0.3s; + height: 1.75em; + overflow: hidden; + border: 1px solid var(--diagram-btn-border); + border-top: none; + border-inline-start: none; + border-end-end-radius: 4px; + width: fit-content; +} + +.diagram-container:hover .diagramHeader, +.diagram-container:focus-within .diagramHeader, +.diagram-container--flipped .diagramHeader { + opacity: 1; + visibility: visible; +} + +@media (hover: none) { + .diagramHeader { + opacity: 1; + visibility: visible; + } +} + +.diagramHeader .diagram-label { + background: var(--diagram-header-bg); + color: var(--diagram-header-color); + font-family: sans-serif; + font-weight: bold; + font-size: 0.85em; + padding: 0 0.75em 0 0.5em; + display: flex; + align-items: center; +} + +.diagram-flip-btn, +.diagramHeader .respec-button-copy-paste { + background: var(--diagram-btn-bg); + border: none; + border-inline-start: 1px solid var(--diagram-btn-border); + color: var(--diagram-btn-color); + cursor: pointer; + padding: 0 0.5em; + text-decoration: none !important; +} + +.diagram-flip-btn { + font-family: monospace; + font-size: 0.8em; +} + +.diagramHeader .respec-button-copy-paste { + position: static; + border-radius: 0; + display: flex; + align-items: center; +} + +.diagram-flip-btn:hover, +.diagramHeader .respec-button-copy-paste:hover { + background: var(--diagram-btn-hover-bg); + color: var(--diagram-btn-hover-color); +} + +.diagram-flip-btn--error { + color: var(--diagram-error-color); + box-shadow: 0 0 6px color-mix(in srgb, var(--diagram-error-border) 50%, transparent); + border-inline-start-color: var(--diagram-error-border); + animation: error-pulse 2.5s ease-in-out infinite; +} + +.diagram-container--error { + border-color: var(--diagram-error-border); + background: var(--diagram-error-bg); + width: 100%; +} + +.diagram-container--error:hover, +.diagram-container--error:focus-within { + border-color: var(--diagram-error-border); +} + +.diagram-container--error .diagram-flip { + min-height: 8em; +} + +.diagram-container--error .diagramHeader { + opacity: 1; + visibility: visible; +} + +figure:has(.diagram-container--error), +.diagram-container--error, +.diagram-container--error * { + text-decoration: none !important; +} + +.diagram-label--error { + background: var(--diagram-error-border) !important; + color: #fff !important; +} + +.diagram-container--flipped .diagram-flip-btn--error { + animation: none; + background: var(--diagram-error-bg); +} + +@keyframes error-pulse { + 0%, 100% { background: var(--diagram-pulse-from); color: var(--diagram-error-color); } + 50% { background: var(--diagram-pulse-to); color: var(--diagram-error-color); } +} + +.diagram-error-front { + color: var(--diagram-error-color); + padding: 2em; + font-family: sans-serif; + font-weight: bold; + font-size: 1.1em; + text-align: center; +} + +.diagram-source-grid { + display: grid; + grid-template-columns: 1.5em 2.5em 1fr; + padding: 0.5em 0; + overflow: auto; + white-space: pre; +} + +.dg-indicator { + text-align: center; + user-select: none; + display: grid; + place-content: center; + border-inline-start: 3px solid transparent; + padding-inline-start: 0.25em; +} + +.dg-indicator--error { + border-inline-start-color: var(--diagram-error-border); + background: var(--diagram-error-bg); +} + +.dg-gutter { + text-align: end; + padding-inline-end: 0.75em; + color: #999; + user-select: none; +} + +.dg-gutter--error { + color: var(--diagram-error-color); + font-weight: bold; + background: var(--diagram-error-bg); +} + +.dg-code { + padding-inline-end: 1em; +} + +.dg-code--error { + background: var(--diagram-error-bg); +} + +.dg-code--hint { + color: var(--diagram-error-color); + font-weight: bold; + font-size: 0.9em; + background: var(--diagram-error-bg); +} + +@media (prefers-reduced-motion: reduce) { + .diagram-container { + transition: none; + } + .diagramHeader { + transition: none; + } + .diagram-flip { + transform-style: flat; + transition: none; + } + .diagram-container--flipped .diagram-flip { + transform: none; + } + .diagram-face { + backface-visibility: visible; + } + .diagram-face--back { + transform: none; + opacity: 0; + visibility: hidden; + } + .diagram-container--flipped .diagram-face--front { + opacity: 0; + visibility: hidden; + } + .diagram-container--flipped .diagram-face--back { + position: relative; + opacity: 1; + visibility: visible; + } + .diagram-flip-btn--error { + animation: none; + } +} + +@media print { + .diagramHeader { + display: none; + } + .diagram-container { + border-color: transparent; + perspective: none; + } + .diagram-flip { + transform-style: flat; + } + .diagram-face { + backface-visibility: visible; + } + .diagram-face--back { + display: none; + } +} +`; diff --git a/tests/karma.conf.base.cjs b/tests/karma.conf.base.cjs index e695ace5fa..d99bb07e6d 100644 --- a/tests/karma.conf.base.cjs +++ b/tests/karma.conf.base.cjs @@ -66,6 +66,7 @@ module.exports = config => { "/js/deps/": "/base/js/deps/", "/base/deps/": "/base/js/deps/", "/worker/respec-worker.js": "/base/worker/respec-worker.js", + "/base/src/core/respec-mermaid.js": "/base/builds/respec-mermaid.js", "/support-files/": "/base/tests/support-files/", }, diff --git a/tests/spec/core/diagrams-spec.js b/tests/spec/core/diagrams-spec.js new file mode 100644 index 0000000000..aa39dd73dd --- /dev/null +++ b/tests/spec/core/diagrams-spec.js @@ -0,0 +1,386 @@ +"use strict"; + +import { + errorFilters, + flushIframes, + makeRSDoc, + makeStandardOps, +} from "../SpecHelper.js"; + +const errorsFilter = errorFilters.filter("core/diagrams"); + +describe("Core - Diagrams", () => { + afterAll(flushIframes); + + describe("Mermaid rendering", () => { + it("renders a mermaid diagram to SVG", async () => { + const body = ` +
+
+            flowchart LR
+              A --> B
+          
+
Test diagram
+
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + const svg = doc.querySelector("#fig-test .diagram-container svg"); + expect(svg).toBeTruthy(); + }); + + it("includes a toolbar with flip and copy buttons on hover", async () => { + const body = ` +
+
+            flowchart LR
+              A --> B
+          
+
Toolbar test
+
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + const header = doc.querySelector("#fig-toolbar .diagramHeader"); + expect(header).toBeTruthy(); + expect(header.textContent).toContain("Mermaid"); + const flipBtn = doc.querySelector("#fig-toolbar .diagram-flip-btn"); + expect(flipBtn).toBeTruthy(); + const copyBtn = doc.querySelector( + "#fig-toolbar .respec-button-copy-paste" + ); + expect(copyBtn).toBeTruthy(); + }); + + it("preserves the original source in the back face", async () => { + const body = ` +
+
+            flowchart LR
+              A --> B
+          
+
Source test
+
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + const code = doc.querySelector("#fig-source .mermaid-source"); + expect(code).toBeTruthy(); + expect(code.textContent).toContain("flowchart LR"); + expect(code.textContent).toContain("A --> B"); + }); + + it("does nothing when no mermaid blocks are present", async () => { + const body = `

No diagrams here.

`; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + const container = doc.querySelector(".diagram-container"); + expect(container).toBeNull(); + }); + + it("silently skips whitespace-only mermaid blocks", async () => { + const body = `
   
`; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + expect(doc.querySelector(".diagram-container")).toBeNull(); + expect(errorsFilter(doc).length).toBe(0); + }); + + it("injects the runtime script", async () => { + const body = ` +
+
+            flowchart LR
+              A --> B
+          
+
Runtime test
+
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + const script = doc.getElementById("respec-diagrams-runtime"); + expect(script).toBeTruthy(); + }); + + it("renders multiple diagrams on one page", async () => { + const body = ` +
+
+            flowchart LR
+              A --> B
+          
+
Diagram A
+
+
+
+            flowchart LR
+              C --> D
+          
+
Diagram B
+
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + const containers = doc.querySelectorAll(".diagram-container"); + expect(containers.length).toBe(2); + }); + }); + + describe("Diagram types", () => { + it("renders a sequence diagram", async () => { + const body = ` +
+
+            sequenceDiagram
+              Alice->>Bob: Hello
+          
+
Sequence
+
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + const svg = doc.querySelector("#fig-seq .diagram-container svg"); + expect(svg).toBeTruthy(); + }); + + it("renders a class diagram", async () => { + const body = ` +
+
+            classDiagram
+              class Animal
+          
+
Class
+
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + const svg = doc.querySelector("#fig-class .diagram-container svg"); + expect(svg).toBeTruthy(); + }); + + it("renders a state diagram", async () => { + const body = ` +
+
+            stateDiagram-v2
+              [*] --> s1
+          
+
State
+
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + const svg = doc.querySelector("#fig-state .diagram-container svg"); + expect(svg).toBeTruthy(); + }); + + it("renders a pie chart", async () => { + const body = ` +
+
+            pie title Pets
+              "Dogs" : 386
+              "Cats" : 85
+          
+
Pie
+
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + const svg = doc.querySelector("#fig-pie .diagram-container svg"); + expect(svg).toBeTruthy(); + }); + }); + + describe("Error handling", () => { + it("displays inline error for invalid mermaid syntax", async () => { + const body = ` +
+
+            this is not valid mermaid syntax !!!
+          
+
Broken diagram
+
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + const errorBlock = doc.querySelector(".diagram-container--error"); + expect(errorBlock).toBeTruthy(); + const errors = errorsFilter(doc); + expect(errors.length).toBeGreaterThan(0); + }); + + it("error cards start flipped showing source", async () => { + const body = ` +
+
+            invalid!!!syntax
+          
+
Error flipped
+
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + const container = doc.querySelector( + ".diagram-container--error.diagram-container--flipped" + ); + expect(container).toBeTruthy(); + }); + + it("preserves source in error block for debugging", async () => { + const body = ` +
+
+            invalid!!!syntax
+          
+
Debug diagram
+
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + const code = doc.querySelector( + ".diagram-container--error .diagram-source-grid" + ); + expect(code).toBeTruthy(); + expect(code.textContent).toContain("invalid!!!syntax"); + }); + + it("stores clean source in data attribute for copy", async () => { + const body = ` +
+
+            bad-syntax!!!
+          
+
Copy source
+
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + const container = doc.querySelector(".diagram-container--error"); + expect(container.dataset.diagramSource).toContain("bad-syntax!!!"); + }); + }); + + describe("Accessibility", () => { + it("flip button has aria-expanded attribute", async () => { + const body = ` +
+
+            flowchart LR
+              A --> B
+          
+
ARIA test
+
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + const btn = doc.querySelector("#fig-aria .diagram-flip-btn"); + expect(btn.getAttribute("aria-expanded")).toBe("false"); + }); + + it("error block flip button starts with aria-expanded=true", async () => { + const body = ` +
+
+            invalid!!!
+          
+
ARIA error
+
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + const btn = doc.querySelector( + ".diagram-container--error .diagram-flip-btn" + ); + expect(btn.getAttribute("aria-expanded")).toBe("true"); + }); + + it("flip button carries data attributes for label toggling", async () => { + const body = ` +
+
+            flowchart LR
+              A --> B
+          
+
Data attrs
+
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + const btn = doc.querySelector("#fig-data .diagram-flip-btn"); + expect(btn.dataset.labelSource).toBeTruthy(); + expect(btn.dataset.labelDiagram).toBeTruthy(); + }); + }); + + describe("Configuration", () => { + it("respects mermaid.theme config", async () => { + const body = ` +
+
+            flowchart LR
+              A --> B
+          
+
Themed diagram
+
+ `; + const ops = makeStandardOps({ mermaid: { theme: "dark" } }, body); + const doc = await makeRSDoc(ops); + const svg = doc.querySelector("#fig-themed .diagram-container svg"); + expect(svg).toBeTruthy(); + }); + + it("renders with default theme when mermaid config is absent", async () => { + const body = ` +
+
+            flowchart LR
+              A --> B
+          
+
Default theme
+
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + const svg = doc.querySelector("#fig-default .diagram-container svg"); + expect(svg).toBeTruthy(); + }); + }); + + describe("Highlight exclusion", () => { + it("does not syntax-highlight the diagram source", async () => { + const body = ` +
+
+            flowchart LR
+              A --> B
+          
+
No highlight
+
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + const sourceCode = doc.querySelector("#fig-no-hl .mermaid-source"); + expect(sourceCode).toBeTruthy(); + expect(sourceCode.classList.contains("hljs")).toBeFalse(); + }); + }); + + describe("Security", () => { + it("does not execute script payloads in diagram labels", async () => { + const body = ` +
+
+            flowchart LR
+              A["<img src=x onerror=window.__xss=true>"] --> B
+          
+
XSS test
+
+ `; + const ops = makeStandardOps(null, body); + const doc = await makeRSDoc(ops); + expect(doc.defaultView.__xss).toBeUndefined(); + }); + }); +}); diff --git a/tests/spec/core/linter-rules/no-uncaptioned-diagram-spec.js b/tests/spec/core/linter-rules/no-uncaptioned-diagram-spec.js new file mode 100644 index 0000000000..39975a8688 --- /dev/null +++ b/tests/spec/core/linter-rules/no-uncaptioned-diagram-spec.js @@ -0,0 +1,50 @@ +"use strict"; + +import { + flushIframes, + makeRSDoc, + makeStandardOps, + warningFilters, +} from "../../SpecHelper.js"; + +const warningsFilter = warningFilters.filter("core/diagrams"); + +describe("Core - Diagrams - uncaptioned diagram warning", () => { + afterAll(flushIframes); + + it("warns when diagram is outside a figure", async () => { + const body = `
flowchart LR
`; + const doc = await makeRSDoc(makeStandardOps(null, body)); + const warnings = warningsFilter(doc); + expect(warnings.length).toBeGreaterThanOrEqual(1); + const match = warnings.find(w => + w.message.includes("must be wrapped in a `
`") + ); + expect(match).toBeTruthy(); + }); + + it("warns when figure is missing a figcaption", async () => { + const body = `
flowchart LR
`; + const doc = await makeRSDoc(makeStandardOps(null, body)); + const warnings = warningsFilter(doc); + const match = warnings.find(w => + w.message.includes("must be wrapped in a `
`") + ); + expect(match).toBeTruthy(); + }); + + it("does not warn when diagram is in a figure with figcaption", async () => { + const body = ` +
+
flowchart LR
+
Test
+
+ `; + const doc = await makeRSDoc(makeStandardOps(null, body)); + const warnings = warningsFilter(doc); + const match = warnings.find(w => + w.message.includes("must be wrapped in a `
`") + ); + expect(match).toBeFalsy(); + }); +}); diff --git a/worker/respec-mermaid.js b/worker/respec-mermaid.js new file mode 100644 index 0000000000..c941d000b1 --- /dev/null +++ b/worker/respec-mermaid.js @@ -0,0 +1,44 @@ +// @ts-check +import mermaid from "mermaid/dist/mermaid.esm.mjs"; + +/** + * @param {{ theme?: string }} options + */ +function initialize({ theme = "neutral" } = {}) { + mermaid.initialize({ + startOnLoad: false, + securityLevel: "strict", + htmlLabels: false, + theme, + }); +} + +initialize(); + +/** + * @param {string} source + * @param {{ id: string }} options + * @returns {Promise<{ svg: string } | { error: string }>} + */ +async function render(source, { id }) { + // Use an off-screen container so mermaid's error SVGs don't leak into view. + // Cannot use display:none as mermaid needs layout for dimension calculation. + const container = document.createElement("div"); + container.id = `${id}-container`; + container.style.position = "absolute"; + container.style.left = "-9999px"; + container.style.top = "-9999px"; + document.body.append(container); + try { + const { svg } = await mermaid.render(id, source, container); + return { svg }; + } catch (err) { + const message = err instanceof Error ? err.message : String(err); + return { error: message }; + } finally { + container.remove(); + document.getElementById(id)?.remove(); + } +} + +self.respecMermaid = { initialize, render }; diff --git a/worker/rollup.config.js b/worker/rollup.config.js index 31d546144e..6b59c0354c 100644 --- a/worker/rollup.config.js +++ b/worker/rollup.config.js @@ -18,4 +18,14 @@ export default [ dir: "builds/", }, }, + { + input: "worker/respec-mermaid.js", + output: { + file: "builds/respec-mermaid.js", + format: "iife", + name: "_respecMermaidBundle", + inlineDynamicImports: true, + }, + plugins: [resolve(), commonjs(), terser()], + }, ];