diff --git a/package-lock.json b/package-lock.json index f3ba04d1..e4b52e9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "framer-motion": "^12.38.0", "highlight.js": "^11.11.1", "lucide-react": "^1.8.0", + "mermaid": "^11.17.2", "monaco-editor": "^0.55.1", "node-pty": "^1.1.0", "node-ssh": "^13.2.1", @@ -62,6 +63,19 @@ "vitest": "^4.1.4" } }, + "node_modules/@antfu/install-pkg": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-2.1.0.tgz", + "integrity": "sha512-sdg9NxU3zR4Mnawfbc/x6GB5Wf17WYud5qOuEuxXjaKpYpMkISSJEjItGebXJ2bQ4DIcly4NYH23mtkGJjvKUw==", + "license": "MIT", + "dependencies": { + "package-manager-detector": "^1.8.0", + "tinyexec": "^1.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@anthropic-ai/claude-code": { "version": "2.1.301", "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-2.1.301.tgz", @@ -455,6 +469,18 @@ "node": ">=6.9.0" } }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", + "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==", + "license": "MIT" + }, + "node_modules/@chevrotain/types": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.1.2.tgz", + "integrity": "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==", + "license": "Apache-2.0" + }, "node_modules/@develar/schema-utils": { "version": "2.6.5", "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", @@ -1577,6 +1603,23 @@ "dev": true, "license": "MIT" }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.7.tgz", + "integrity": "sha512-JZHlwdID+dy+lTgbYC8NEC4zeugqeYsc6jewvzb4c58kHauJn+X7rNwQjxz5p2qSjqaEeQoLkCIQ9v/H4PK0/w==", + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^2.0.1", + "@iconify/types": "^2.0.0", + "import-meta-resolve": "^4.2.0" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1831,6 +1874,15 @@ "node": ">= 10.0.0" } }, + "node_modules/@mermaid-js/parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.2.1.tgz", + "integrity": "sha512-n12NohV3mrUyUL2o93IgG/ifeW9FTyeJn3zDxkhwa8MJ9Fxg3HQMlA3RiGmD/3UnJvheztkjjQAjA2T4LmUcpw==", + "license": "MIT", + "dependencies": { + "@chevrotain/types": "~11.1.2" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.3.tgz", @@ -3135,6 +3187,259 @@ "assertion-error": "^2.0.1" } }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "license": "MIT" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "license": "MIT", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-65Emv9fQiQQqphLlRkuQ5ypPsOmWPhtBGCMv61JDPEPMvsx+gzhGf74yw1a78xFKPj6zw4AgQICJoQv0vK9M2w==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "license": "MIT" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.4.tgz", + "integrity": "sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "license": "MIT" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.12.tgz", + "integrity": "sha512-Qe/KWYhEiIIxGs7HrAAjMfShxKldx19SJtr5zu53f3afPsdZNz7HHtdTLXo/kqeiWNXVycI24kSnfzBYkTzpgw==", + "license": "MIT" + }, + "node_modules/@types/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-kVd74ta9eof3eJOvbNd1vGKS/XERRyQbT26Og63hIsvDO84cjD5gEOhsXf26w3FSoNlPVz84DOFcKv/oou+fMw==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.9.tgz", + "integrity": "sha512-0sE1406XBYJGiqD3AusTl9ZqC//2mIXix51tbom25gDCA8ri4xnSZg28CaSE8Srl6FClqABUKfsn0qghgdepMA==", + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "node_modules/@types/debug": { "version": "4.1.13", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", @@ -3182,6 +3487,12 @@ "@types/node": "*" } }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, "node_modules/@types/hast": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", @@ -3323,6 +3634,16 @@ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "license": "ISC" }, + "node_modules/@upsetjs/venn.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@upsetjs/venn.js/-/venn.js-2.0.0.tgz", + "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==", + "license": "MIT", + "optionalDependencies": { + "d3-selection": "^3.0.0", + "d3-transition": "^3.0.1" + } + }, "node_modules/@vitejs/plugin-react": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", @@ -4595,167 +4916,691 @@ "dev": true, "license": "ISC", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "license": "MIT", + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/cpu-features": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", + "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "buildcheck": "~0.0.6", + "nan": "^2.19.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/crc": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", + "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "buffer": "^5.1.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cytoscape": { + "version": "3.34.4", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.34.4.tgz", + "integrity": "sha512-msY99tMY8iWTg+sIPGfSqDcPNOt4xcj+v/K/7qUFaCUxxlqnMkUYLBjRUzn9W5CU/R2hrmtJa5+UtovrEM2ctg==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "license": "MIT", + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", + "license": "MIT" + }, + "node_modules/d3": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.2.tgz", + "integrity": "sha512-ZKb/mONPVKzaO5UlcWcwN8bS4ezvQaYu+bsou+XgNABdGC800kwH651ADTbbTxMPHW13UlvbQafdmJQDk1w7Mg==", + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.5", + "d3-axis": "^3.0.1", + "d3-brush": "^3.0.1", + "d3-chord": "^3.0.2", + "d3-color": "^3.1.1", + "d3-contour": "^4.0.3", + "d3-delaunay": "^6.0.6", + "d3-dispatch": "^3.0.2", + "d3-drag": "^3.0.1", + "d3-dsv": "^3.0.2", + "d3-ease": "^3.0.2", + "d3-fetch": "^3.0.2", + "d3-force": "^3.0.1", + "d3-format": "^3.1.3", + "d3-geo": "^3.1.2", + "d3-hierarchy": "^3.1.3", + "d3-interpolate": "^3.0.2", + "d3-path": "^3.1.1", + "d3-polygon": "^3.0.2", + "d3-quadtree": "^3.0.2", + "d3-random": "^3.0.2", + "d3-scale": "^4.0.3", + "d3-scale-chromatic": "^3.1.1", + "d3-selection": "^3.0.1", + "d3-shape": "^3.2.1", + "d3-time": "^3.1.1", + "d3-time-format": "^4.1.1", + "d3-timer": "^3.0.2", + "d3-transition": "^3.0.2", + "d3-zoom": "^3.0.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.5.tgz", + "integrity": "sha512-Th8qGXoBalmWsstpkuhmBuTsBQIFqYOJtjlBvNVvBfJ8E3uYusBUwUR8HU20U0qKKQkgCvKyR1YjiozWadk5QA==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.1.tgz", + "integrity": "sha512-E5QCXfaf/uJZEHHiqoWRq+bQEm2PzoVFv2rDC7CZ3wwI0MjfMW3w83J3ubobG4IJj3lShAI5eiPJhOu/8wHofA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.1.tgz", + "integrity": "sha512-L5Glqa/Gb7LU3ndIOMtA5mDzlJ0aXKp5ELP2X09ctWF23jO7+faIUVDbmaIi+D6k3EemXFWn1F3lkfguExoVhA==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.2.tgz", + "integrity": "sha512-cGmADo5M2PTi3ewJZ2lcE32eURd4JmHtQ5JwRIUcas5L1SXqufG9yPyjWp8fW0gRi7aZi9bgbZPFZO1eyZ+1CQ==", + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.1.tgz", + "integrity": "sha512-lfab8x0Bj0UO7SNsY8dZSbNIA6ipbkE3X0NG9nqjCu6CfCHZseuDnOSJY7hidKrjpP++i1M2qT+FlAdPKYRilA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.3.tgz", + "integrity": "sha512-5DYURc8KwXPOhSUBblU0P9rVcArsSPxPOxsfO0Eud0s28oU0sB2e80aRHRL9AZW3usT8ikFKyIHpHHLNWLdRVA==", + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.6.tgz", + "integrity": "sha512-DMQIEvSJhZCB6pwedcXaAlQ+MeKU5LlM2XakKtc73b/VM+RBiI1SRXycOEOKg2jsKj4zZ9F0KGYG6nCGwwWu/g==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.2.tgz", + "integrity": "sha512-sAzH4Y4nsLo8eSqw5jKiZt+LU2wZmL/qS+OkxQGjhwazB/D7TY34R/QK1eshfGr7cZEKymKZ/Haem7m+lu98Sg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.1.tgz", + "integrity": "sha512-OWeoayCq8J69hUIjvL+Mb6Q6Oaj/fyHV5XaXByEUAG+JUjIfTGDnk3qGpcXrD3VmQ3BSV96fcbM6fqq7C0tbPA==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.2.tgz", + "integrity": "sha512-FUnXhX7I4/AXyRCZzwfZTDGSq8PGZ4oR0tiCBqNGOZMsDrQ+6sIWNQnBzcCGhzJKZ6uCvCezTwflUqI209RRiQ==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-B5znKqRmKROAN4oHWQ50W/qisVjA9GXh2WMsVKdFVp4DxWmcrOsweqgUOUVsv5th96kwaZlokU6SlV4J3gak1w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.2.tgz", + "integrity": "sha512-1wGF62bgjMRbF6mqbD+6rRWBL2hwWVYQjFO2UP3Wm1nIAvJZKaQxXM5caVY7athoCa8wIq9lCf7g44lPWFk50g==", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.1.tgz", + "integrity": "sha512-kAaaHLFebBYwgi5cYBXAI8vKrQFMCD2MEswj2QuPKKbdU1wYLc8WXtS7O8F/b6POCIe3gJjY6hkDstHi1yMETQ==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.3.tgz", + "integrity": "sha512-+F+/49swqmGuQJmSKnVMIDH/QkEFV5li9QRj0xm/KhyAUBv099O5PrvpQFxBp24PYph17Mnvt9mjKQV0ii0nMA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.2.tgz", + "integrity": "sha512-5R8ZgDQorIxmauv7R6gpE/ndz+Z0qAZssigq0aXFdvPHdY41RqtW02jrRUQAL724NdPABYXo1+gQEGBBr7uKNg==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.3.tgz", + "integrity": "sha512-GRajN5ZcnEwYFWqqdzhG1qJ3+fDg6WWzk47TJg7ePY7yvYAgRfD2r1mUx+wviBOWuCwamCUzjwMSdato43jbGA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.2.tgz", + "integrity": "sha512-IRzGU62DtxhvsnSTWMQIdnoZ6gA+BCWwBon1EJI6SjoN595799b6z/QRBzXfFhE8iupUPp3042yBNo0STPqnzQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-jsbgV8zIT38ReB9O7X7gcLndTQRONSqNNd+oGIaTBdUug4r5jFmp0N6RY9R5GWbdUDucn0qoTGyfjapT4tDhcg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-WAUvPudGU1Dn4mDPfb6+JnnbeVEV53hG/BoT+bxts6AkZsOpTEiV1jHYX+2vtwCICNFG9Wxv8QS+eW1zE/Dwfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.2.tgz", + "integrity": "sha512-9Lu9mqJKRbIk0d6mKDjpvPURaPdwHVK5CYbcdd0/4vBgz4ldz/7TIOse3kNERsByGNfld58KEco507AztJWyTQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.2.tgz", + "integrity": "sha512-PxJi9LNV+7mulaYO77nd+oGurXGL1fhedXjHnB3pWLUcqDnQtF4VASoqnQGHgoXLByVdv5QJ/2kfwkGn5HTS8g==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "license": "BSD-3-Clause", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "license": "ISC" + }, + "node_modules/d3-scale": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.3.tgz", + "integrity": "sha512-66cWobCNzcMvlfoaDHCxuIv9TBUWG1BLOWdv9kHHFuCr1JdqAWPvTmsaEYDD5NRMyWZ5+7u0GqiCDfMXMqh3hg==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.1.tgz", + "integrity": "sha512-lA5gfN2yMvmChubmptnDiBfeGpHD7Yb1EbvCz0TZE0WHXhyR6W2allPuhKtzV5ILQRqeOdefF0qYtERReLUBDg==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-scale": "1 - 4" }, "engines": { "node": ">=12" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "license": "MIT", + "node_modules/d3-selection": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.1.tgz", + "integrity": "sha512-ODSf35vqCrlCfp7hrm0eZjvOosSXSYLjqvIlhMBV9q9VOx7EBJG6pc+uXBswEZNJgCGJ+qnKYcWutbZDIxe/Xg==", + "license": "ISC", "engines": { - "node": ">=0.8" + "node": ">=12" } }, - "node_modules/clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "dev": true, - "license": "MIT", + "node_modules/d3-shape": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.1.tgz", + "integrity": "sha512-dm2o+lbKdzg9tY7GEag/dB9hcbodLfwkE/DkvqbELJXaMz0yKzWWhZqhbI7CKubukA7HnRyz0avdtvi7EPxPvw==", + "license": "ISC", "dependencies": { - "mimic-response": "^1.0.0" + "d3-path": "^3.1.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=12" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", + "node_modules/d3-time": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.1.tgz", + "integrity": "sha512-Ze7rjrhMzdeulh0/hjcfFj7XLAHOfowtEMfsoG6DU866HPynQNW7Wfen6hzXcqKTv8XzxvF6Hzr5XD6bMHr2ug==", + "license": "ISC", "dependencies": { - "color-name": "~1.1.4" + "d3-array": "2 - 3" }, "engines": { - "node": ">=7.0.0" + "node": ">=12" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "license": "MIT", + "node_modules/d3-time-format": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.1.tgz", + "integrity": "sha512-22XaqjyCjpqgda20erhsOjGmC3vvYwTNYniKAd45W5UWLDF43moeOuV9qYpRqobIlsnK0UhnKZjUUJm3VgORUA==", + "license": "ISC", "dependencies": { - "delayed-stream": "~1.0.0" + "d3-time": "1 - 3" }, "engines": { - "node": ">= 0.8" - } - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" + "node": ">=12" } }, - "node_modules/compare-version": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", - "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", - "dev": true, - "license": "MIT", + "node_modules/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-1Il1aliroVSErrv5aulRBxAY+7+DnaoLIWGd1C6SsBTWDeobWiP/V69vwb8vT6abR1kKwaNe1196pmaQ1fK90A==", + "license": "ISC", "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/cpu-features": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", - "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", - "hasInstallScript": true, - "optional": true, + "node_modules/d3-transition": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.2.tgz", + "integrity": "sha512-RcjCsdFgyFoDB+QXLC5m60ZkTN0k9zH+ovoLlTdJnDS1AbfbcuowiM4pCKSTOclNibS4f4J4cbv2m7O7y8NTOQ==", + "license": "ISC", "dependencies": { - "buildcheck": "~0.0.6", - "nan": "^2.19.0" + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" }, "engines": { - "node": ">=10.0.0" + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" } }, - "node_modules/crc": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", - "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", - "dev": true, - "license": "MIT", - "optional": true, + "node_modules/d3-zoom": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.1.tgz", + "integrity": "sha512-kfjD3BDQu/FOatpZ6JdMSyIoDwTxIpI29ySTI2J5ZlDaH1WbuJGQJ2Q9oJkWTEZEW98j6idezoomSD3Cn2zwiw==", + "license": "ISC", "dependencies": { - "buffer": "^5.1.0" + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" } }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, + "node_modules/dagre-d3-es": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz", + "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==", "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" + "d3": "^7.9.0", + "lodash-es": "^4.17.21" } }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, + "node_modules/dayjs": { + "version": "1.11.23", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.23.tgz", + "integrity": "sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==", "license": "MIT" }, "node_modules/debug": { @@ -4878,6 +5723,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delaunator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz", + "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -5558,6 +6412,18 @@ "node": ">= 0.4" } }, + "node_modules/es-toolkit": { + "version": "1.53.1", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.53.1.tgz", + "integrity": "sha512-VhMc6IXmexu8iYFfL2aaAww6IvJTdHlqBLruLoEUs33YD4IcP5DdZcLOybMsMmmLsiy1c8PsiLsrM0DdIuuRUA==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks", + "tests/types", + "tests/browser-compat" + ] + }, "node_modules/es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", @@ -5721,6 +6587,15 @@ "dev": true, "license": "MIT" }, + "node_modules/fastdom": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastdom/-/fastdom-1.0.12.tgz", + "integrity": "sha512-LB+xjSTEbjHE1cWsxu+tN2Xqr1kpi+V9aADI7sVM5ZMaXyYGPHULQMzpJMYqOTULK/73pUkWVzzObFRBkPr+hg==", + "license": "MIT", + "dependencies": { + "strictdom": "^1.0.1" + } + }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -6085,6 +6960,12 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", + "license": "MIT" + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -6362,7 +7243,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -6392,6 +7272,16 @@ ], "license": "BSD-3-Clause" }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -6444,6 +7334,15 @@ "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", "license": "MIT" }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/ip-address": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", @@ -6703,6 +7602,31 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/katex": { + "version": "0.16.47", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz", + "integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -6713,6 +7637,17 @@ "json-buffer": "3.0.1" } }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "license": "MIT" + }, "node_modules/lazy-val": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", @@ -6987,6 +7922,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "license": "MIT" + }, "node_modules/lodash.escaperegexp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", @@ -7462,6 +8403,57 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mermaid": { + "version": "11.17.2", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.17.2.tgz", + "integrity": "sha512-V6K3C8EBdEsPFZXSKMJe6ppQOENxuHARr9GvHX4hh47lAbhMRD9qf4oEK7LoaRQxULMa80/qt5gHO73aCleBBg==", + "license": "MIT", + "dependencies": { + "@braintree/sanitize-url": "^7.1.2", + "@iconify/utils": "^3.0.2", + "@mermaid-js/parser": "^1.2.1", + "@types/d3": "^7.4.3", + "@upsetjs/venn.js": "^2.0.0", + "cytoscape": "^3.34.0", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.14", + "dayjs": "^1.11.21", + "dompurify": "^3.3.3", + "es-toolkit": "^1.45.1", + "fastdom": "1.0.12", + "katex": "^0.16.47", + "khroma": "^2.1.0", + "marked": "^16.3.0", + "roughjs": "^4.6.6", + "stylis": "^4.3.6", + "ts-dedent": "^2.2.0", + "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0" + } + }, + "node_modules/mermaid/node_modules/dompurify": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.17.tgz", + "integrity": "sha512-B/pSlYVHQ58kqISD/yDjuJ1yfzI34m1gF4lAod2bwSRhiN4K5Cl3H8Af7hqB2PmTL911tdEudnnXn5FnhRoz0w==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/mermaid/node_modules/marked": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", + "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/micromark": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", @@ -8868,6 +9860,12 @@ "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/package-manager-detector": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.9.0.tgz", + "integrity": "sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA==", + "license": "MIT" + }, "node_modules/parse-entities": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", @@ -8893,6 +9891,12 @@ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", "license": "MIT" }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", + "license": "MIT" + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -9023,6 +10027,22 @@ "node": ">=10.4.0" } }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", + "license": "MIT" + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "license": "MIT", + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, "node_modules/postcss": { "version": "8.5.9", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", @@ -9480,6 +10500,12 @@ "node": ">=8.0" } }, + "node_modules/robust-predicates": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", + "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==", + "license": "Unlicense" + }, "node_modules/rolldown": { "version": "1.0.0-rc.15", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz", @@ -9521,6 +10547,24 @@ "dev": true, "license": "MIT" }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", + "license": "MIT", + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -9863,6 +10907,12 @@ "dev": true, "license": "MIT" }, + "node_modules/strictdom": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strictdom/-/strictdom-1.0.1.tgz", + "integrity": "sha512-cEmp9QeXXRmjj/rVp9oyiqcvyocWab/HaoN4+bwFeZ7QzykJD6L3yD4v12K1x0tHpqRqVpJevN3gW7kyM39Bqg==", + "license": "MIT" + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -9963,6 +11013,12 @@ "inline-style-parser": "0.2.7" } }, + "node_modules/stylis": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz", + "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==", + "license": "MIT" + }, "node_modules/sumchecker": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", @@ -10153,10 +11209,9 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", - "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", - "dev": true, + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", "license": "MIT", "engines": { "node": ">=18" @@ -10239,6 +11294,15 @@ "utf8-byte-length": "^1.0.1" } }, + "node_modules/ts-dedent": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.3.0.tgz", + "integrity": "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==", + "license": "MIT", + "engines": { + "node": ">=6.10" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -10478,6 +11542,19 @@ "dev": true, "license": "MIT" }, + "node_modules/uuid": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.3.tgz", + "integrity": "sha512-Sc5u1VVln93y1Xnz4R+CunOZc7R9LUcTmhj7Rc7Eg3HVcf7rVn2FOoa812fe0W8p3jD7xeoGSYbfqCGGH2B6xw==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, "node_modules/verror": { "version": "1.10.1", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", diff --git a/package.json b/package.json index 3c842e8b..1823bfd9 100644 --- a/package.json +++ b/package.json @@ -170,6 +170,7 @@ "framer-motion": "^12.38.0", "highlight.js": "^11.11.1", "lucide-react": "^1.8.0", + "mermaid": "^11.17.2", "monaco-editor": "^0.55.1", "node-pty": "^1.1.0", "node-ssh": "^13.2.1", diff --git a/resources/mcp-bridge.js b/resources/mcp-bridge.js index e647618e..acd35b0f 100644 --- a/resources/mcp-bridge.js +++ b/resources/mcp-bridge.js @@ -462,7 +462,7 @@ const TOOLS = [ { name: 'submit_review', description: - "Finish a review of this worktree's branch: a verdict plus a summary, covering every comment of yours that is still open. Leave inline comments with add_review_comment first and put cross-cutting points in the summary. Call it once per review round.\n\nIf you were opened by `request_review`, the review goes back to the tab that asked for it — you do not need to work out who that is. Otherwise it goes to the most recently active Chat tab other than yours; pass `tab` (from list_tabs) to choose. Terminal tabs cannot receive reviews. Depending on the user's settings it is delivered right away or held as a draft in the Review tab for the user to edit and send; the result says which, and whether delivery failed.", + "Finish a review of this worktree's branch: a verdict, a summary, and a briefing, covering every comment of yours that is still open. Leave inline comments with add_review_comment first and put cross-cutting points in the summary. Call it once per review round.\n\nThe summary and comments go to the author agent. The briefing does not: it is shown to the user in the Review tab, above the summary, and is written for a human who has probably not read the change yet.\n\nIf you were opened by `request_review`, the review goes back to the tab that asked for it — you do not need to work out who that is. Otherwise it goes to the most recently active Chat tab other than yours; pass `tab` (from list_tabs) to choose. Terminal tabs cannot receive reviews. Depending on the user's settings it is delivered right away or held as a draft in the Review tab for the user to edit and send; the result says which, and whether delivery failed.", inputSchema: { type: 'object', properties: { @@ -474,7 +474,13 @@ const TOOLS = [ }, summary: { type: 'string', - description: 'Overall assessment: what the change does well, what must change, and anything not tied to one line.' + description: + 'For the author agent: a short, actionable list of what must change and anything not tied to one line. Do not repeat the briefing — the user reads that, the author reads this.' + }, + briefing: { + type: 'string', + description: + "Markdown for the user, not the author: the case for this change, so they can understand it and decide whether to merge without reading the diff first. Cover what it does and why, how it works (a diagram for any flow, state or data-shape change — a ```mermaid code block renders as a real diagram; mark the nodes this change touches with :::added, :::changed or :::removed, which follow the user's theme), your critique ranked by severity with blockers clearly separated from non-blocking notes, and a closing recommendation. Optional, but leave it out only for a trivial change." }, tab: { type: 'string', @@ -1109,6 +1115,7 @@ async function handleToolCall(name, args) { const r = await callControl('POST', '/reviews/submit', { verdict: args.verdict, summary: typeof args.summary === 'string' ? args.summary : '', + briefing: typeof args.briefing === 'string' && args.briefing.trim() ? args.briefing : undefined, tab: typeof args.tab === 'string' && args.tab.trim() ? args.tab.trim() : undefined }) const what = diff --git a/resources/mcp-bridge.test.js b/resources/mcp-bridge.test.js index d4864180..c12b9a41 100644 --- a/resources/mcp-bridge.test.js +++ b/resources/mcp-bridge.test.js @@ -546,11 +546,17 @@ describe('mcp-bridge review tools', () => { expect(draft.result.content[0].text).toMatch(/held as a draft.*tab author/) const sent = await callTool('submit_review', { verdict: 'approve', summary: 's' }) expect(sent.result.content[0].text).toMatch(/delivered to tab author \(woke it\)/) - const failed = await callTool('submit_review', { verdict: 'comment', summary: 's', tab: 'term' }) + const failed = await callTool('submit_review', { + verdict: 'comment', + summary: 's', + briefing: '## What it does', + tab: 'term' + }) expect(failed.result.content[0].text).toMatch(/NOT delivered: not supported yet/) expect(stub.captured.filter((c) => c.url === '/reviews/submit').at(-1).body).toEqual({ verdict: 'comment', summary: 's', + briefing: '## What it does', tab: 'term' }) }) diff --git a/src/main/build-initial-state.test.ts b/src/main/build-initial-state.test.ts index 51cb52b2..bc2854c2 100644 --- a/src/main/build-initial-state.test.ts +++ b/src/main/build-initial-state.test.ts @@ -95,7 +95,8 @@ describe('buildInitialAppState', () => { { id: 'c3', filePath: 'a.ts', line: 1, anchor, status: 'resolved', resolvedBy: { kind: 'robot' }, resolvedAt: 6 } ], submissions: [ - { id: 's1', summary: '', commentIds: [], state: 'sent', verdict: 'approve', headSha: 7 } + { id: 's1', summary: '', commentIds: [], state: 'sent', verdict: 'approve', headSha: 7, briefing: { md: 'x' } }, + { id: 's2', summary: '', commentIds: [], state: 'sent', verdict: 'approve', briefing: '## What it does' } ], requests: [ { id: 'r1', requester: author, reviewer: author, context: 'c', source: 'agent', reused: false, createdAt: 1 }, @@ -113,6 +114,8 @@ describe('buildInitialAppState', () => { expect(wt.comments[2]).not.toHaveProperty('resolvedBy') expect(wt.comments[2].resolvedAt).toBe(6) expect(wt.submissions?.[0]).not.toHaveProperty('headSha') + expect(wt.submissions?.[0]).not.toHaveProperty('briefing') + expect(wt.submissions?.[1].briefing).toBe('## What it does') expect(wt.requests?.map((r) => r.id)).toEqual(['r1']) }) }) diff --git a/src/main/build-initial-state.ts b/src/main/build-initial-state.ts index a4769774..abe54bda 100644 --- a/src/main/build-initial-state.ts +++ b/src/main/build-initial-state.ts @@ -140,10 +140,10 @@ function sanitizeReviews(raw: unknown): ReviewsState['byPath'] { (s.verdict === 'approve' || s.verdict === 'changes-requested' || s.verdict === 'comment') ) .map((s: ReviewSubmission) => { - if (s.headSha === undefined || typeof s.headSha === 'string') return s - const { headSha: _drop, ...rest } = s - void _drop - return rest + const next = { ...s } + if (next.headSha !== undefined && typeof next.headSha !== 'string') delete next.headSha + if (next.briefing !== undefined && typeof next.briefing !== 'string') delete next.briefing + return next }) : [] const requests: ReviewRequest[] = Array.isArray(rawRequests) diff --git a/src/main/control-server.ts b/src/main/control-server.ts index 51f1b08b..43d9e1d2 100644 --- a/src/main/control-server.ts +++ b/src/main/control-server.ts @@ -158,7 +158,7 @@ export interface ReviewQueries { submit: ( worktreePath: string, callerTabId: string, - input: { verdict: ReviewVerdict; summary: string; targetTabId?: string } + input: { verdict: ReviewVerdict; summary: string; briefing?: string; targetTabId?: string } ) => SubmitReviewResult | Promise /** Open a reviewer Chat tab for the caller and hand it the request. */ request: ( @@ -1046,8 +1046,14 @@ async function handleRequest( }) } const summary = typeof body.summary === 'string' ? body.summary : '' + const briefing = typeof body.briefing === 'string' ? body.briefing : undefined const tab = typeof body.tab === 'string' && body.tab.trim() ? body.tab.trim() : undefined - const result = await deps.reviews.submit(wt, terminalId, { verdict, summary, targetTabId: tab }) + const result = await deps.reviews.submit(wt, terminalId, { + verdict, + summary, + briefing, + targetTabId: tab + }) if ('error' in result) return sendJson(res, 400, { error: result.error }) const { submission, delivery } = result return sendJson(res, 200, { diff --git a/src/main/persistence.ts b/src/main/persistence.ts index b29363e0..f9795a4c 100644 --- a/src/main/persistence.ts +++ b/src/main/persistence.ts @@ -481,6 +481,8 @@ Prefer these over blind curl/fetch — or shelling out to \`open \`, which Show your work with screenshots — this applies in Chat tabs, whose transcript is rendered markdown; a terminal tab is an xterm and will show the markdown as literal text. Every screenshot_tab result comes back with the path it was saved to, and embedding an absolute image path as markdown (\`![caption](/path/to/shot.png)\`) renders it inline — so when you've changed something visual, end your reply with the screenshot that proves it rather than only describing it. This works for any image file on this machine, not just screenshot_tab captures. Append a \`#w=\` fragment to set the displayed width: \`![caption](path#w=500)\` is 500px, \`#w=50%\` fills half the column, \`#w=400x300\` sets both dimensions, and no fragment means a default size. Use the fragment rather than a \`|\` size spec in the alt text — inside a markdown table a pipe is a column delimiter, so the cell splits and the embed renders as broken literal text. Be selective: embed the one or two shots that show the result, not every frame you took while finding your way around. +Chat tabs also draw diagrams: a fenced \`\`\`mermaid code block (flowchart, sequenceDiagram, stateDiagram-v2, classDiagram, erDiagram, …) renders as a real diagram, with a toggle to its source. Reach for one when a flow, a sequence of calls, a state machine or an architecture is easier seen than read; a small ASCII sketch is still fine for something tiny. Quote node labels that contain punctuation (\`A["parse()"]\`) — a syntax error falls back to showing the source. To color nodes, use Ness's theme-aware classes instead of hex colors, which you can't check against the user's theme: \`A:::added\` (green), \`A:::changed\` (amber), \`A:::removed\` (red, struck through), \`A:::focus\` (accent) — in flowcharts, state and class diagrams. Use them to mean something, like which parts a change touches, not for decoration. In a terminal tab it is just text. + Ness also exposes shell tabs for long-running processes — anything that wouldn't naturally exit within a few seconds (dev servers, watchers, \`tail -f\`, REPL-style tools, long builds). Drive them via the ness-control shell tools (scoped to this worktree only): - create_shell: spawn a shell tab, optionally with a command to run (\`zsh -ilc \`). Returns an id — keep it for later reads. diff --git a/src/main/review-service.test.ts b/src/main/review-service.test.ts index 261b0048..36b376d9 100644 --- a/src/main/review-service.test.ts +++ b/src/main/review-service.test.ts @@ -298,6 +298,27 @@ describe('submitReview', () => { expect(store.getSnapshot().state.reviews.byPath[WT].submissions![0].state).toBe('sent') }) + it('keeps the briefing on the submission but out of the delivered message', () => { + const { store, deps, deliver } = setup([comment('mine')]) + const result = submitReview(deps, WT, author, { + verdict: 'approve', + summary: 'lgtm', + briefing: ' ## What it does\nAdds a cache. ', + holdAsDraft: false + }) + if ('error' in result) throw new Error(result.error) + expect(store.getSnapshot().state.reviews.byPath[WT].submissions![0].briefing).toBe( + '## What it does\nAdds a cache.' + ) + expect(deliver.mock.calls[0][2]).not.toContain('Adds a cache') + }) + + it('omits a blank briefing', () => { + const { store, deps } = setup([comment('mine')]) + submitReview(deps, WT, author, { verdict: 'comment', summary: 's', briefing: ' ', holdAsDraft: true }) + expect(store.getSnapshot().state.reviews.byPath[WT].submissions![0]).not.toHaveProperty('briefing') + }) + it('records a failed delivery with a readable reason', () => { const { store, deps } = setup([comment('mine')], () => ({ ok: false, diff --git a/src/main/review-service.ts b/src/main/review-service.ts index a0a6fad4..144fde5b 100644 --- a/src/main/review-service.ts +++ b/src/main/review-service.ts @@ -338,6 +338,8 @@ export function requestReview( export interface SubmitReviewInput { verdict: ReviewVerdict summary: string + /** Explanation for the user; shown in the Review tab, never delivered. */ + briefing?: string /** Explicit author tab. Must be a Chat tab in the worktree other than * the reviewer's. */ targetTabId?: string @@ -385,11 +387,13 @@ export function submitReview( const requester = reviewRequester(state, worktreePath, reviewerTabId) const targetTabId = input.targetTabId ?? requester ?? pickReviewTarget(tabs, reviewerTabId) ?? undefined + const briefing = input.briefing?.trim() const submission: ReviewSubmission = { id: randomUUID(), author, verdict: input.verdict, summary: input.summary.trim(), + ...(briefing ? { briefing } : {}), commentIds, createdAt: Date.now(), state: 'draft', diff --git a/src/renderer/components/JsonModeChat.tsx b/src/renderer/components/JsonModeChat.tsx index dd2e0fed..8285a5f2 100644 --- a/src/renderer/components/JsonModeChat.tsx +++ b/src/renderer/components/JsonModeChat.tsx @@ -14,7 +14,9 @@ import ReactMarkdown, { defaultUrlTransform, type Components } from 'react-markd import { rehypeHighlightShared } from '../rehype-highlight-shared' import remarkGfm from 'remark-gfm' import { remarkInsight } from '../remark-insight' +import { remarkMermaid } from '../remark-mermaid' import { InsightCard } from './InsightCard' +import { MarkdownPre } from './MermaidDiagram' import { AlertOctagon, AlertTriangle, @@ -95,7 +97,8 @@ import { useFindController } from './JsonModeChatFind' -const REMARK_PLUGINS = [remarkGfm, remarkInsight] +// remarkMermaid must run after remarkInsight, which rebuilds the tree. +const REMARK_PLUGINS = [remarkGfm, remarkInsight, remarkMermaid] const REHYPE_PLUGINS = [rehypeHighlightShared, rehypeColorHex] /** Markdown `![alt](…)` in a message. An agent that just took a browser @@ -160,7 +163,8 @@ function MarkdownImage({ // element. const MARKDOWN_COMPONENTS = { 'insight-card': InsightCard, - img: MarkdownImage + img: MarkdownImage, + pre: MarkdownPre } as unknown as Components // react-markdown's default transform blanks any src whose protocol isn't diff --git a/src/renderer/components/MermaidDiagram.tsx b/src/renderer/components/MermaidDiagram.tsx new file mode 100644 index 00000000..7b538bb9 --- /dev/null +++ b/src/renderer/components/MermaidDiagram.tsx @@ -0,0 +1,275 @@ +import { memo, useEffect, useState, useSyncExternalStore, type ReactNode } from 'react' +import { Code2, Workflow } from 'lucide-react' + +// Mermaid is ~1MB, so it loads on the first diagram rather than at boot. +// Renders are serialized because mermaid.initialize is global config and +// mermaid.render measures text in a shared scratch element; two renders +// interleaving across an await can draw with each other's theme. + +type MermaidApi = typeof import('mermaid').default + +let mermaidPromise: Promise | null = null +function loadMermaid(): Promise { + mermaidPromise ??= import('mermaid').then((m) => m.default) + return mermaidPromise +} + +const THEME_VARS = [ + 'app', + 'panel', + 'panel-raised', + 'surface', + 'border', + 'border-strong', + 'fg', + 'fg-bright', + 'muted', + 'accent' +] as const + +type ThemeColors = Record<(typeof THEME_VARS)[number], string> + +function readThemeColors(): ThemeColors { + const style = getComputedStyle(document.documentElement) + return Object.fromEntries( + THEME_VARS.map((v) => [v, style.getPropertyValue(`--color-${v}`).trim()]) + ) as ThemeColors +} + +// Theme switches rewrite [data-theme] and, for custom themes and uiScale, +// inline styles on . One observer for every diagram on screen. +let themeKey = '' +const themeListeners = new Set<() => void>() +let themeObserver: MutationObserver | null = null +function subscribeTheme(cb: () => void): () => void { + themeListeners.add(cb) + if (!themeObserver) { + themeObserver = new MutationObserver(() => { + const next = currentThemeKey() + if (next === themeKey) return + themeKey = next + for (const l of themeListeners) l() + }) + themeObserver.observe(document.documentElement, { + attributes: true, + attributeFilter: ['data-theme', 'style', 'class'] + }) + } + return () => { + themeListeners.delete(cb) + if (themeListeners.size === 0) { + themeObserver?.disconnect() + themeObserver = null + // Nothing watches the theme now, so the key would go stale. + themeKey = '' + } + } +} +function getThemeKey(): string { + if (!themeKey) themeKey = currentThemeKey() + return themeKey +} + +function currentThemeKey(): string { + return `${JSON.stringify(readThemeColors())}|${getComputedStyle(document.documentElement).fontSize}` +} + +function isDark(hex: string): boolean { + const m = /^#([0-9a-f]{6})/i.exec(hex) + if (!m) return true + const n = parseInt(m[1], 16) + const lum = 0.299 * ((n >> 16) & 255) + 0.587 * ((n >> 8) & 255) + 0.114 * (n & 255) + return lum < 128 +} + +const MIN_SCALE = 0.65 + +function mermaidConfig(c: ThemeColors): Parameters[0] { + // Mermaid sizes text in px. Derive it from the root font-size so + // diagrams follow uiScale like the rest of the UI (13px at the default). + const fontPx = Math.round(parseFloat(getComputedStyle(document.documentElement).fontSize) * 0.8125) + return { + startOnLoad: false, + securityLevel: 'strict', + suppressErrorRendering: true, + theme: 'base', + fontFamily: getComputedStyle(document.body).fontFamily, + sequence: { actorFontSize: fontPx, messageFontSize: fontPx, noteFontSize: fontPx }, + themeVariables: { + darkMode: isDark(c.app), + background: c.panel, + fontSize: `${fontPx}px`, + primaryColor: c.surface, + primaryTextColor: c['fg-bright'], + primaryBorderColor: c['border-strong'], + secondaryColor: c['panel-raised'], + tertiaryColor: c.panel, + lineColor: c.muted, + textColor: c.fg, + mainBkg: c.surface, + nodeBorder: c['border-strong'], + clusterBkg: c['panel-raised'], + clusterBorder: c.border, + edgeLabelBackground: c.panel, + actorBkg: c.surface, + actorBorder: c['border-strong'], + actorTextColor: c['fg-bright'], + signalColor: c.muted, + signalTextColor: c.fg, + labelBoxBkgColor: c.surface, + noteBkgColor: c['panel-raised'], + noteTextColor: c.fg, + noteBorderColor: c.accent + } + } +} + +type RenderResult = { svg: string } | { error: string } + +// Keyed by theme + source so chat rows that unmount and remount while +// scrolling show their diagram straight away instead of re-rendering. +const MAX_CACHE = 64 +const cache = new Map() +let queue: Promise = Promise.resolve() +let seq = 0 + +function renderMermaid(source: string, key: string): Promise { + const run = async (): Promise => { + const hit = cache.get(key) + if (hit) return hit + let result: RenderResult + try { + const mermaid = await loadMermaid() + mermaid.initialize(mermaidConfig(readThemeColors())) + const { svg } = await mermaid.render(`ness-mermaid-${++seq}`, source) + // Mermaid emits width="100%" capped at the natural width, so a wide + // diagram shrinks until its labels are unreadable. Floor the shrink + // and let the container scroll past that. + const natural = Number(/max-width:\s*([\d.]+)px/.exec(svg)?.[1]) + result = { + svg: natural + ? svg.replace(/max-width:\s*[\d.]+px;?/, `max-width: ${natural}px; min-width: ${Math.round(natural * MIN_SCALE)}px;`) + : svg + } + } catch (err) { + result = { error: err instanceof Error ? err.message : String(err) } + } + cache.set(key, result) + if (cache.size > MAX_CACHE) cache.delete(cache.keys().next().value as string) + return result + } + const p = queue.then(run, run) + queue = p + return p +} + +/** A ```mermaid block drawn as a diagram, with a toggle back to its + * source. Shows the source while rendering and when it fails to parse, + * so a typo costs the picture, never the content. `fallback` is the + * normal code block for that source. */ +export const MermaidDiagram = memo(function MermaidDiagram({ + source, + fallback +}: { + source: string + fallback: ReactNode +}): JSX.Element { + const theme = useSyncExternalStore(subscribeTheme, getThemeKey) + const key = `${theme}\n${source}` + const [result, setResult] = useState(() => cache.get(key) ?? null) + const [showSource, setShowSource] = useState(false) + + useEffect(() => { + const hit = cache.get(key) + if (hit) return setResult(hit) + let cancelled = false + void renderMermaid(source, key).then((r) => { + if (!cancelled) setResult(r) + }) + return () => { + cancelled = true + } + }, [key, source]) + + if (!result || showSource || 'error' in result) { + return ( +
+ {fallback} + {result && 'error' in result && ( +
Diagram didn’t render: {result.error.split('\n')[0]}
+ )} + {result && 'svg' in result && ( + setShowSource(false)} label="Show diagram"> + + + )} +
+ ) + } + + return ( +
+
{ + if ((e.target as Element).closest('a')) e.preventDefault() + }} + dangerouslySetInnerHTML={{ __html: result.svg }} + /> + setShowSource(true)} label="Show source"> + + +
+ ) +}) + +function ToggleButton({ + onClick, + label, + children +}: { + onClick: () => void + label: string + children: ReactNode +}): JSX.Element { + return ( + + ) +} + +type HastNode = { + type: string + tagName?: string + value?: string + properties?: Record + children?: HastNode[] +} + +function hastText(node: HastNode): string { + if (node.type === 'text') return node.value ?? '' + return (node.children ?? []).map(hastText).join('') +} + +/** react-markdown `pre` override: a finished ```mermaid block (marked by + * remarkMermaid) becomes a diagram, everything else renders as usual. */ +export function MarkdownPre({ + node, + ...props +}: React.HTMLAttributes & { node?: HastNode }): JSX.Element { + const code = node?.children?.find((c) => c.tagName === 'code') + const pre =
+  if (code?.properties?.dataMermaid !== 'closed') return pre
+  return 
+}
diff --git a/src/renderer/components/ReviewMarkdown.tsx b/src/renderer/components/ReviewMarkdown.tsx
index a69f2355..1b452cb0 100644
--- a/src/renderer/components/ReviewMarkdown.tsx
+++ b/src/renderer/components/ReviewMarkdown.tsx
@@ -2,12 +2,15 @@ import { memo, useLayoutEffect, useRef, useState } from 'react'
 import ReactMarkdown, { type Components } from 'react-markdown'
 import remarkGfm from 'remark-gfm'
 import { rehypeHighlightShared } from '../rehype-highlight-shared'
+import { remarkMermaid } from '../remark-mermaid'
+import { MarkdownPre } from './MermaidDiagram'
 
 /** GFM for review comments, replies, summaries and request context. Same
  *  `.markdown` typography as chat. No rehype-raw, so raw HTML stays inert,
- *  and the default urlTransform still blanks `javascript:` and friends. */
+ *  and the default urlTransform still blanks `javascript:` and friends.
+ *  ```mermaid blocks render as diagrams. */
 
-const REMARK_PLUGINS = [remarkGfm]
+const REMARK_PLUGINS = [remarkGfm, remarkMermaid]
 const REHYPE_PLUGINS = [rehypeHighlightShared]
 
 // Rows that hold markdown are themselves clickable (jump to the diff), so
@@ -16,6 +19,7 @@ const REHYPE_PLUGINS = [rehypeHighlightShared]
 // browser and silently drops anything else — so relative file links
 // render as text rather than as links that do nothing.
 const COMPONENTS: Components = {
+  pre: MarkdownPre,
   a: ({ href, children }) =>
     href && /^(https?:|mailto:)/i.test(href) ? (
        e.stopPropagation()}>
diff --git a/src/renderer/components/ReviewSubmissions.tsx b/src/renderer/components/ReviewSubmissions.tsx
index 224e5684..81bb6725 100644
--- a/src/renderer/components/ReviewSubmissions.tsx
+++ b/src/renderer/components/ReviewSubmissions.tsx
@@ -1,5 +1,5 @@
 import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
-import { Bot, ChevronDown, ChevronRight, MessageSquare, Pencil, Send, SquareTerminal, X } from 'lucide-react'
+import { BookOpen, Bot, ChevronDown, ChevronRight, MessageSquare, Pencil, Send, SquareTerminal, X } from 'lucide-react'
 import type { ReviewSubmission, ReviewVerdict } from '../types'
 import { useBackend } from '../backend'
 import { useWorktreePanes } from '../store'
@@ -177,6 +177,7 @@ export function SubmissionCard({
   const [summary, setSummary] = useState(submission.summary)
   const [verdict, setVerdict] = useState(submission.verdict)
   const [sending, setSending] = useState(false)
+  const [showAuthorSummary, setShowAuthorSummary] = useState(false)
   const reviewerTabId = submission.author.kind === 'agent' ? submission.author.tabId : undefined
   const target = submission.targetTabId
     ? (tabLabels.get(submission.targetTabId) ?? 'a closed tab')
@@ -276,6 +277,19 @@ export function SubmissionCard({
       {submission.state === 'failed' && submission.error && (
         
{submission.error}
)} + {submission.briefing && ( +
+
+ + Briefing for you +
+ {fullSummary ? ( + + ) : ( + + )} +
+ )} {editing ? (
@@ -318,7 +332,25 @@ export function SubmissionCard({
) : ( - submission.summary && ( + submission.summary && + (submission.briefing ? ( +
+ + {showAuthorSummary && ( +
+ +
+ )} +
+ ) : (
{fullSummary ? ( @@ -326,7 +358,7 @@ export function SubmissionCard({ )}
- ) + )) )} {children}
diff --git a/src/renderer/remark-mermaid.test.ts b/src/renderer/remark-mermaid.test.ts new file mode 100644 index 00000000..79759bd2 --- /dev/null +++ b/src/renderer/remark-mermaid.test.ts @@ -0,0 +1,66 @@ +import { describe, it, expect } from 'vitest' +import { unified } from 'unified' +import remarkParse from 'remark-parse' +import remarkGfm from 'remark-gfm' +import type { Code, Root } from 'mdast' +import { closedMermaidBodies, remarkMermaid } from './remark-mermaid' +import { remarkInsight } from './remark-insight' + +function codeNodes(md: string, plugins: Array<() => (tree: Root, file?: { value?: unknown }) => void>): Code[] { + let processor = unified().use(remarkParse).use(remarkGfm) + for (const p of plugins) processor = processor.use(p) + const tree = processor.runSync(processor.parse(md), md) as Root + const out: Code[] = [] + const walk = (n: { type: string; children?: unknown[] }): void => { + if (n.type === 'code') out.push(n as Code) + for (const c of (n.children ?? []) as Array<{ type: string; children?: unknown[] }>) walk(c) + } + walk(tree) + return out +} + +const closed = (c: Code): boolean => + (c.data?.hProperties as Record | undefined)?.dataMermaid === 'closed' + +describe('closedMermaidBodies', () => { + it('collects only fences that have closed', () => { + const md = '```mermaid\ngraph TD\n A-->B\n```\n\n```mermaid\ngraph LR\n X-->' + expect([...closedMermaidBodies(md)]).toEqual(['graph TD\nA-->B']) + }) + + it('needs the same fence character and at least the same length', () => { + expect(closedMermaidBodies('````mermaid\na\n```\n').size).toBe(0) + expect(closedMermaidBodies('~~~mermaid\na\n```\n').size).toBe(0) + expect(closedMermaidBodies('~~~mermaid\na\n~~~~\n').size).toBe(1) + }) +}) + +describe('remarkMermaid', () => { + it('marks a finished mermaid block and leaves one still streaming', () => { + const done = codeNodes('```mermaid\ngraph TD\n A-->B\n```\n', [remarkMermaid]) + expect(done.map(closed)).toEqual([true]) + const streaming = codeNodes('```mermaid\ngraph TD\n A-->B\n', [remarkMermaid]) + expect(streaming.map(closed)).toEqual([false]) + }) + + it('ignores other languages', () => { + expect(codeNodes('```ts\nconst a = 1\n```\n', [remarkMermaid]).map(closed)).toEqual([false]) + }) + + it('marks a block nested under a list item, where remark strips the indent', () => { + const md = '- How it works:\n ```mermaid\n graph TD\n A-->B\n ```\n- next point\n' + expect(codeNodes(md, [remarkMermaid]).map(closed)).toEqual([true]) + const streaming = '- How it works:\n ```mermaid\n graph TD\n A-->B\n' + expect(codeNodes(streaming, [remarkMermaid]).map(closed)).toEqual([false]) + }) + + it('marks a block inside a blockquote', () => { + const md = '> ```mermaid\n> graph TD\n> A-->B\n> ```\n' + expect(codeNodes(md, [remarkMermaid]).map(closed)).toEqual([true]) + }) + + it('still finds a block inside an Insight card, which is re-parsed', () => { + const md = '★ Insight ─────\n```mermaid\ngraph TD\n A-->B\n```\n─────\n' + expect(codeNodes(md, [remarkInsight, remarkMermaid]).map(closed)).toEqual([true]) + }) +}) diff --git a/src/renderer/remark-mermaid.ts b/src/renderer/remark-mermaid.ts new file mode 100644 index 00000000..70283a4b --- /dev/null +++ b/src/renderer/remark-mermaid.ts @@ -0,0 +1,75 @@ +import type { Root, Code, Parent } from 'mdast' + +// Marks ```mermaid blocks whose closing fence has arrived, so the renderer +// draws a diagram only for finished source. A block still streaming in is +// left as a code block: rendering a half-written diagram on every token +// would fail to parse most of the time and re-run mermaid per token. +// +// Closedness is read from the raw source rather than node positions, +// because remarkInsight re-parses Insight bodies and their positions are +// relative to the segment, not the message. A block counts as closed when +// some closed mermaid fence in the source has the same body. +// +// Bodies are compared line-trimmed: remark strips the list-item and fence +// indent from `code.value`, so a fence nested under a bullet or inside a +// blockquote has different leading whitespace in the raw source. + +const OPEN_RE = /^(`{3,}|~{3,})\s*mermaid\s*$/i +const FENCE_RE = /^(`{3,}|~{3,})\s*$/ + +/** A source line without its container prefix: indentation and any + * blockquote markers. */ +function unprefixed(line: string): string { + return line.replace(/^(?:\s*>)*\s*/, '') +} + +export function mermaidKey(body: string): string { + return body + .split('\n') + .map((l) => l.trim()) + .join('\n') + .trim() +} + +export function closedMermaidBodies(source: string): Set { + const closed = new Set() + const lines = source.split('\n').map(unprefixed) + for (let i = 0; i < lines.length; i++) { + const open = OPEN_RE.exec(lines[i])?.[1] + if (!open) continue + for (let j = i + 1; j < lines.length; j++) { + const fence = FENCE_RE.exec(lines[j])?.[1] + if (fence && fence[0] === open[0] && fence.length >= open.length) { + closed.add(mermaidKey(lines.slice(i + 1, j).join('\n'))) + i = j + break + } + } + } + return closed +} + +function walk(node: Root | Parent, closed: Set): void { + for (const child of node.children) { + if (child.type === 'code') { + const code = child as Code + if (code.lang?.toLowerCase() === 'mermaid' && closed.has(mermaidKey(code.value))) { + code.data = { + ...code.data, + hProperties: { ...code.data?.hProperties, dataMermaid: 'closed' } + } + } + } else if ('children' in child) { + walk(child as Parent, closed) + } + } +} + +export function remarkMermaid() { + return (tree: Root, file?: { value?: unknown }) => { + const source = typeof file?.value === 'string' ? file.value : null + if (source === null || !/mermaid/i.test(source)) return + const closed = closedMermaidBodies(source) + if (closed.size > 0) walk(tree, closed) + } +} diff --git a/src/renderer/styles.css b/src/renderer/styles.css index 98266e32..f5276a4c 100644 --- a/src/renderer/styles.css +++ b/src/renderer/styles.css @@ -410,6 +410,23 @@ mark.find-hit-current { display: none; } +/* Semantic node classes for mermaid diagrams (`A:::added`). Agents can't + see the theme, so instead of picking hex colors they name a meaning and + the theme supplies the color. !important because mermaid's own