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()],
+ },
];