diff --git a/MosaicIQ/package-lock.json b/MosaicIQ/package-lock.json index 08624bd..b2b92a5 100644 --- a/MosaicIQ/package-lock.json +++ b/MosaicIQ/package-lock.json @@ -15,7 +15,9 @@ "lucide-react": "^1.7.0", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-markdown": "^10.1.0", "recharts": "^3.8.1", + "remark-gfm": "^4.0.1", "tailwindcss": "^4.2.2" }, "devDependencies": { @@ -609,13 +611,54 @@ "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", "license": "MIT" }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "license": "MIT" }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.2.14", - "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -629,12 +672,24 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, "node_modules/@types/use-sync-external-store": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", "license": "MIT" }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, "node_modules/@vitejs/plugin-react": { "version": "4.7.0", "dev": true, @@ -654,6 +709,16 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/baseline-browser-mapping": { "version": "2.10.13", "dev": true, @@ -716,6 +781,56 @@ ], "license": "CC-BY-4.0" }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -725,6 +840,16 @@ "node": ">=6" } }, + "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/convert-source-map": { "version": "2.0.0", "dev": true, @@ -732,7 +857,6 @@ }, "node_modules/csstype": { "version": "3.2.3", - "devOptional": true, "license": "MIT" }, "node_modules/d3-array": { @@ -858,7 +982,6 @@ }, "node_modules/debug": { "version": "4.4.3", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -878,6 +1001,28 @@ "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", "license": "MIT" }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "license": "Apache-2.0", @@ -885,6 +1030,19 @@ "node": ">=8" } }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.329", "dev": true, @@ -958,12 +1116,40 @@ "node": ">=6" } }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/eventemitter3": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", "license": "MIT" }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/fdir": { "version": "6.5.0", "license": "MIT", @@ -1002,6 +1188,56 @@ "version": "4.2.11", "license": "ISC" }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/immer": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz", @@ -1012,6 +1248,12 @@ "url": "https://opencollective.com/immer" } }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, "node_modules/internmap": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", @@ -1021,6 +1263,62 @@ "node": ">=12" } }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/jiti": { "version": "2.6.1", "license": "MIT", @@ -1100,6 +1398,16 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "dev": true, @@ -1124,9 +1432,851 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", - "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -1150,6 +2300,31 @@ "dev": true, "license": "MIT" }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "license": "ISC" @@ -1190,6 +2365,16 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/react": { "version": "19.2.4", "license": "MIT", @@ -1214,6 +2399,33 @@ "license": "MIT", "peer": true }, + "node_modules/react-markdown": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, "node_modules/react-redux": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", @@ -1290,6 +2502,72 @@ "redux": "^5.0.0" } }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/reselect": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", @@ -1357,6 +2635,48 @@ "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, "node_modules/tailwindcss": { "version": "4.2.2", "license": "MIT" @@ -1392,6 +2712,26 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/typescript": { "version": "5.8.3", "dev": true, @@ -1404,6 +2744,93 @@ "node": ">=14.17" } }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "dev": true, @@ -1442,6 +2869,34 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/victory-vendor": { "version": "37.3.6", "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", @@ -1540,6 +2995,16 @@ "version": "3.1.1", "dev": true, "license": "ISC" + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/MosaicIQ/package.json b/MosaicIQ/package.json index 4e8f850..9d639d5 100644 --- a/MosaicIQ/package.json +++ b/MosaicIQ/package.json @@ -17,7 +17,9 @@ "lucide-react": "^1.7.0", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-markdown": "^10.1.0", "recharts": "^3.8.1", + "remark-gfm": "^4.0.1", "tailwindcss": "^4.2.2" }, "devDependencies": { diff --git a/MosaicIQ/src-tauri/src/agent/gateway.rs b/MosaicIQ/src-tauri/src/agent/gateway.rs index 05a1fb7..e04de19 100644 --- a/MosaicIQ/src-tauri/src/agent/gateway.rs +++ b/MosaicIQ/src-tauri/src/agent/gateway.rs @@ -1,21 +1,39 @@ use std::pin::Pin; +use std::sync::Arc; use futures::{future::BoxFuture, Stream, StreamExt}; use rig::{ + agent::MultiTurnStreamItem, client::completion::CompletionClient, - completion::{CompletionModel, Message}, + completion::Message, + message::ToolChoice, providers::openai, - streaming::StreamedAssistantContent, + streaming::{StreamedAssistantContent, StreamingPrompt}, }; +use tauri::{AppHandle, Wry}; +use tokio::sync::mpsc; +use crate::agent::tools::terminal_command::{AgentCommandExecutor, RunTerminalCommandTool}; use crate::agent::AgentRuntimeConfig; use crate::error::AppError; +use crate::state::PendingAgentToolApprovals; -const SYSTEM_PROMPT: &str = "You are MosaicIQ's terminal chat assistant. Answer concisely in plain text. Do not claim to run tools, commands, or file operations. If the request is unclear, ask a short clarifying question."; +const SYSTEM_PROMPT: &str = "You are MosaicIQ's terminal chat assistant. Answer concisely in plain text. Use the available terminal command tool whenever current workspace data or live MosaicIQ terminal actions would improve the answer. Never claim to have run a command unless the tool actually ran it. If the request is unclear, ask a short clarifying question."; +const MAX_TOOL_TURNS: usize = 4; /// Streaming text output from the upstream chat provider. pub type ChatGatewayStream = Pin> + Send>>; +#[derive(Clone)] +pub struct AgentToolRuntimeContext { + pub app_handle: AppHandle, + pub command_executor: Arc, + pub pending_approvals: Arc, + pub workspace_id: String, + pub request_id: String, + pub session_id: String, +} + /// Trait used by the agent service so tests can inject a deterministic gateway. pub trait ChatGateway: Clone + Send + Sync + 'static { /// Start a streaming chat turn for the given config, prompt, and prior history. @@ -25,6 +43,7 @@ pub trait ChatGateway: Clone + Send + Sync + 'static { prompt: String, context_messages: Vec, history: Vec, + tool_runtime: AgentToolRuntimeContext, ) -> BoxFuture<'static, Result>; } @@ -39,9 +58,9 @@ impl ChatGateway for RigChatGateway { prompt: String, context_messages: Vec, history: Vec, + tool_runtime: AgentToolRuntimeContext, ) -> BoxFuture<'static, Result> { Box::pin(async move { - let _task_profile = runtime.task_profile; let api_key = runtime.api_key.unwrap_or_default(); let client = openai::CompletionsClient::builder() .api_key(api_key) @@ -49,25 +68,62 @@ impl ChatGateway for RigChatGateway { .build() .map_err(|error| AppError::ProviderInit(error.to_string()))?; - let model = client.completion_model(runtime.model); + let history = compose_request_messages(context_messages, history); + let tool = RunTerminalCommandTool { + app_handle: tool_runtime.app_handle, + command_executor: tool_runtime.command_executor, + pending_approvals: tool_runtime.pending_approvals, + workspace_id: tool_runtime.workspace_id, + request_id: tool_runtime.request_id, + session_id: tool_runtime.session_id, + }; - let upstream = model - .completion_request(Message::user(prompt)) - .messages(compose_request_messages(context_messages, history)) - .preamble(SYSTEM_PROMPT.to_string()) + let mut rig_stream = client + .agent(runtime.model) + .preamble(SYSTEM_PROMPT) .temperature(0.2) - .stream() - .await - .map_err(|error| AppError::ProviderRequest(error.to_string()))?; + .tool(tool) + .tool_choice(ToolChoice::Auto) + .default_max_turns(MAX_TOOL_TURNS) + .build() + .stream_prompt(prompt) + .with_history(history) + .multi_turn(MAX_TOOL_TURNS) + .await; - let stream = upstream.filter_map(|item| async move { - match item { - Ok(StreamedAssistantContent::Text(text)) => Some(Ok(text.text)), - Ok(_) => None, - Err(error) => Some(Err(AppError::ProviderRequest(error.to_string()))), + let (sender, receiver) = mpsc::unbounded_channel::>(); + tauri::async_runtime::spawn(async move { + let mut saw_text = false; + + while let Some(item) = rig_stream.next().await { + match item { + Ok(MultiTurnStreamItem::StreamAssistantItem( + StreamedAssistantContent::Text(text), + )) => { + saw_text = true; + if sender.send(Ok(text.text)).is_err() { + return; + } + } + Ok(MultiTurnStreamItem::FinalResponse(final_response)) => { + if !saw_text && !final_response.response().is_empty() { + let _ = sender.send(Ok(final_response.response().to_string())); + } + return; + } + Ok(_) => {} + Err(error) => { + let _ = sender.send(Err(map_streaming_error(error))); + return; + } + } } }); + let stream = futures::stream::unfold(receiver, |mut receiver| async move { + receiver.recv().await.map(|item| (item, receiver)) + }); + let stream: ChatGatewayStream = Box::pin(stream); Ok(stream) }) @@ -81,6 +137,10 @@ fn compose_request_messages( context_messages.into_iter().chain(history).collect() } +fn map_streaming_error(error: rig::agent::StreamingError) -> AppError { + AppError::ProviderRequest(error.to_string()) +} + #[cfg(test)] mod tests { use rig::completion::Message; diff --git a/MosaicIQ/src-tauri/src/agent/mod.rs b/MosaicIQ/src-tauri/src/agent/mod.rs index 64e6ba9..97c437a 100644 --- a/MosaicIQ/src-tauri/src/agent/mod.rs +++ b/MosaicIQ/src-tauri/src/agent/mod.rs @@ -5,15 +5,18 @@ mod panel_context; mod routing; mod service; mod settings; +mod tools; mod types; -pub use gateway::{ChatGateway, RigChatGateway}; +pub use gateway::{AgentToolRuntimeContext, ChatGateway, RigChatGateway}; pub use service::AgentService; pub(crate) use settings::AgentSettingsService; pub use types::{ default_task_defaults, AgentConfigStatus, AgentDeltaEvent, AgentErrorEvent, AgentResultEvent, - AgentRuntimeConfig, AgentStoredSettings, AgentTaskRoute, ChatPanelContext, - ChatPromptRequest, ChatStreamStart, PreparedChatTurn, RemoteProviderSettings, - SaveAgentRuntimeConfigRequest, TaskProfile, UpdateRemoteApiKeyRequest, - AGENT_SETTINGS_STORE_PATH, DEFAULT_REMOTE_BASE_URL, DEFAULT_REMOTE_MODEL, + AgentRuntimeConfig, AgentStoredSettings, AgentTaskRoute, AgentToolApprovalRequiredEvent, + AgentToolCommandEvent, AgentToolResultEvent, ChatPanelContext, ChatPromptRequest, + ChatStreamStart, PreparedChatTurn, RemoteProviderSettings, + ResolveAgentToolApprovalRequest, SaveAgentRuntimeConfigRequest, TaskProfile, + UpdateRemoteApiKeyRequest, AGENT_SETTINGS_STORE_PATH, DEFAULT_REMOTE_BASE_URL, + DEFAULT_REMOTE_MODEL, }; diff --git a/MosaicIQ/src-tauri/src/agent/panel_context.rs b/MosaicIQ/src-tauri/src/agent/panel_context.rs index aa57592..e78c2d9 100644 --- a/MosaicIQ/src-tauri/src/agent/panel_context.rs +++ b/MosaicIQ/src-tauri/src/agent/panel_context.rs @@ -50,7 +50,7 @@ pub(crate) fn compact_panel_payload(panel: &PanelPayload) -> Value { } } -fn panel_type(panel: &PanelPayload) -> &'static str { +pub(crate) fn panel_type(panel: &PanelPayload) -> &'static str { match panel { PanelPayload::Company { .. } => "company", PanelPayload::Error { .. } => "error", diff --git a/MosaicIQ/src-tauri/src/agent/tools/mod.rs b/MosaicIQ/src-tauri/src/agent/tools/mod.rs new file mode 100644 index 0000000..50d80b6 --- /dev/null +++ b/MosaicIQ/src-tauri/src/agent/tools/mod.rs @@ -0,0 +1 @@ +pub(crate) mod terminal_command; diff --git a/MosaicIQ/src-tauri/src/agent/tools/terminal_command.rs b/MosaicIQ/src-tauri/src/agent/tools/terminal_command.rs new file mode 100644 index 0000000..88064d5 --- /dev/null +++ b/MosaicIQ/src-tauri/src/agent/tools/terminal_command.rs @@ -0,0 +1,451 @@ +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; +use std::time::Duration; + +use rig::completion::ToolDefinition; +use rig::tool::Tool; +use serde::Deserialize; +use serde_json::json; +use tauri::{AppHandle, Emitter, Runtime}; +use tokio::time::timeout; + +use crate::agent::panel_context::{compact_panel_payload, panel_type}; +use crate::agent::{ + AgentToolApprovalRequiredEvent, AgentToolCommandEvent, AgentToolResultEvent, +}; +use crate::error::AppError; +use crate::state::PendingAgentToolApprovals; +use crate::terminal::{ + ExecuteTerminalCommandRequest, TerminalCommandResponse, TerminalCommandService, +}; + +const APPROVAL_TIMEOUT: Duration = Duration::from_secs(60); + +pub trait AgentCommandExecutor: Send + Sync { + fn execute<'a>( + &'a self, + request: ExecuteTerminalCommandRequest, + ) -> Pin + Send + 'a>>; +} + +impl AgentCommandExecutor for TerminalCommandService { + fn execute<'a>( + &'a self, + request: ExecuteTerminalCommandRequest, + ) -> Pin + Send + 'a>> { + Box::pin(async move { self.execute(request).await }) + } +} + +#[derive(Clone)] +pub struct RunTerminalCommandTool { + pub app_handle: AppHandle, + pub command_executor: Arc, + pub pending_approvals: Arc, + pub workspace_id: String, + pub request_id: String, + pub session_id: String, +} + +#[derive(Debug, Deserialize)] +pub struct RunTerminalCommandArgs { + pub command: String, +} + +#[derive(Debug, thiserror::Error)] +pub enum RunTerminalCommandToolError { + #[error("{0}")] + App(#[from] AppError), + #[error("failed to emit terminal event: {0}")] + Emit(String), + #[error("failed to serialize tool result: {0}")] + Serialize(String), +} + +impl Tool for RunTerminalCommandTool { + const NAME: &'static str = "run_terminal_command"; + + type Error = RunTerminalCommandToolError; + type Args = RunTerminalCommandArgs; + type Output = String; + + async fn definition(&self, _prompt: String) -> ToolDefinition { + ToolDefinition { + name: Self::NAME.to_string(), + description: "Run a MosaicIQ terminal slash command. Supported read-only commands: /search, /news, /analyze, /fa, /cf, /dvd, /em, /portfolio. Supported write commands requiring user approval: /buy, /sell, /cash. /clear and /help are forbidden.".to_string(), + parameters: json!({ + "type": "object", + "properties": { + "command": { + "type": "string", + "description": "A full MosaicIQ slash command such as /search AAPL or /fa AAPL annual. Must start with /." + } + }, + "required": ["command"], + "additionalProperties": false + }), + } + } + + async fn call(&self, args: Self::Args) -> Result { + let command = normalize_command(&args.command); + let Some(command_name) = command_name(&command) else { + return Ok(serialize_status_only_tool_result( + &command, + "rejected", + "Commands must start with '/'.", + )?); + }; + + if !is_allowed_agent_command(command_name) { + return Ok(serialize_status_only_tool_result( + &command, + "rejected", + "This command is not available to the agent.", + )?); + } + + self.emit_command(&command)?; + + if is_write_command(command_name) { + let approved = self.await_approval(&command).await?; + if !approved { + return Ok(serialize_tool_result(&json!({ + "command": command, + "status": "denied", + "summary": "The user denied approval for this portfolio-changing command." + }))?); + } + } + + let response = self + .command_executor + .execute(ExecuteTerminalCommandRequest { + workspace_id: self.workspace_id.clone(), + input: command.clone(), + }) + .await; + + self.emit_result(response.clone())?; + serialize_response_tool_result(command, response) + } +} + +impl RunTerminalCommandTool { + fn emit_command(&self, command: &str) -> Result<(), RunTerminalCommandToolError> { + self.app_handle + .emit( + "agent_tool_command", + AgentToolCommandEvent { + workspace_id: self.workspace_id.clone(), + request_id: self.request_id.clone(), + session_id: self.session_id.clone(), + command: command.to_string(), + }, + ) + .map_err(|error| RunTerminalCommandToolError::Emit(error.to_string())) + } + + fn emit_result( + &self, + response: TerminalCommandResponse, + ) -> Result<(), RunTerminalCommandToolError> { + self.app_handle + .emit( + "agent_tool_result", + AgentToolResultEvent { + workspace_id: self.workspace_id.clone(), + request_id: self.request_id.clone(), + session_id: self.session_id.clone(), + response, + }, + ) + .map_err(|error| RunTerminalCommandToolError::Emit(error.to_string())) + } + + async fn await_approval(&self, command: &str) -> Result { + let (approval_id, receiver) = self.pending_approvals.register()?; + + self.app_handle + .emit( + "agent_tool_approval_required", + AgentToolApprovalRequiredEvent { + workspace_id: self.workspace_id.clone(), + request_id: self.request_id.clone(), + session_id: self.session_id.clone(), + approval_id: approval_id.clone(), + command: command.to_string(), + title: "Approve portfolio command".to_string(), + message: format!( + "The agent wants to run a portfolio-changing command:\n\n{}\n\nApprove this action to continue.", + command + ), + }, + ) + .map_err(|error| RunTerminalCommandToolError::Emit(error.to_string()))?; + + let result = timeout(APPROVAL_TIMEOUT, receiver).await; + match result { + Ok(Ok(approved)) => Ok(approved), + Ok(Err(_)) => Ok(false), + Err(_) => { + self.pending_approvals.cancel(&approval_id); + Ok(false) + } + } + } +} + +pub(crate) fn normalize_command(input: &str) -> String { + input.split_whitespace().collect::>().join(" ") +} + +pub(crate) fn command_name(command: &str) -> Option<&str> { + let command = command.trim(); + if !command.starts_with('/') { + return None; + } + + command.split_whitespace().next() +} + +pub(crate) fn is_write_command(command_name: &str) -> bool { + matches!( + command_name.to_ascii_lowercase().as_str(), + "/buy" | "/sell" | "/cash" + ) +} + +pub(crate) fn is_allowed_agent_command(command_name: &str) -> bool { + matches!( + command_name.to_ascii_lowercase().as_str(), + "/search" + | "/news" + | "/analyze" + | "/fa" + | "/cf" + | "/dvd" + | "/em" + | "/portfolio" + | "/buy" + | "/sell" + | "/cash" + ) +} + +fn serialize_response_tool_result( + command: String, + response: TerminalCommandResponse, +) -> Result { + match response { + TerminalCommandResponse::Text { content, .. } => serialize_tool_result(&json!({ + "command": command, + "status": "executed", + "responseKind": "text", + "summary": content, + })), + TerminalCommandResponse::Panel { panel } => serialize_tool_result(&json!({ + "command": command, + "status": "executed", + "responseKind": "panel", + "panelType": panel_type(&panel), + "summary": compact_panel_payload(&panel), + })), + } +} + +fn serialize_tool_result(value: &serde_json::Value) -> Result { + serde_json::to_string(value) + .map_err(|error| RunTerminalCommandToolError::Serialize(error.to_string())) +} + +fn serialize_status_only_tool_result( + command: &str, + status: &str, + summary: &str, +) -> Result { + serialize_tool_result(&json!({ + "command": command, + "status": status, + "summary": summary, + })) +} + + +#[cfg(test)] +mod tests { + use std::future::Future; + use std::pin::Pin; + use std::sync::atomic::{AtomicUsize, Ordering}; + use std::sync::Arc; + + use rig::tool::Tool; + use tauri::test::{mock_builder, mock_context, noop_assets, MockRuntime}; + + use super::{ + command_name, is_allowed_agent_command, is_write_command, normalize_command, + AgentCommandExecutor, RunTerminalCommandArgs, RunTerminalCommandTool, + }; + use crate::state::PendingAgentToolApprovals; + use crate::terminal::{ + Company, ExecuteTerminalCommandRequest, PanelPayload, TerminalCommandResponse, + }; + + struct FakeExecutor { + response: TerminalCommandResponse, + calls: AtomicUsize, + } + + impl FakeExecutor { + fn new(response: TerminalCommandResponse) -> Self { + Self { + response, + calls: AtomicUsize::new(0), + } + } + } + + impl AgentCommandExecutor for FakeExecutor { + fn execute<'a>( + &'a self, + _request: ExecuteTerminalCommandRequest, + ) -> Pin + Send + 'a>> { + self.calls.fetch_add(1, Ordering::Relaxed); + let response = self.response.clone(); + Box::pin(async move { response }) + } + } + + #[test] + fn classifies_commands_correctly() { + assert!(is_allowed_agent_command("/search")); + assert!(is_write_command("/buy")); + assert!(!is_allowed_agent_command("/clear")); + assert_eq!(command_name("/search AAPL"), Some("/search")); + assert_eq!(normalize_command(" /search AAPL "), "/search AAPL"); + } + + #[tokio::test] + async fn read_only_command_executes_and_returns_panel_summary() { + let app = build_test_app(); + let executor = Arc::new(FakeExecutor::new(TerminalCommandResponse::Panel { + panel: PanelPayload::Company { + data: Company { + symbol: "AAPL".to_string(), + name: "Apple Inc.".to_string(), + price: 200.0, + change: 1.0, + change_percent: 0.5, + market_cap: 1.0, + volume: None, + volume_label: None, + pe: None, + eps: None, + high52_week: None, + low52_week: None, + profile: None, + price_chart: None, + price_chart_ranges: None, + }, + }, + })); + + let tool = RunTerminalCommandTool { + app_handle: app.handle().clone(), + command_executor: executor.clone(), + pending_approvals: Arc::new(PendingAgentToolApprovals::new()), + workspace_id: "workspace-1".to_string(), + request_id: "request-1".to_string(), + session_id: "session-1".to_string(), + }; + + let result = tool + .call(RunTerminalCommandArgs { + command: "/search AAPL".to_string(), + }) + .await + .expect("tool result"); + + assert!(result.contains("\"status\":\"executed\"")); + assert!(result.contains("\"panelType\":\"company\"")); + assert_eq!(executor.calls.load(Ordering::Relaxed), 1); + } + + #[tokio::test] + async fn denied_write_command_does_not_execute() { + let app = build_test_app(); + let approvals = Arc::new(PendingAgentToolApprovals::new()); + let executor = Arc::new(FakeExecutor::new(TerminalCommandResponse::Text { + content: "ok".to_string(), + portfolio: None, + })); + + let tool = RunTerminalCommandTool { + app_handle: app.handle().clone(), + command_executor: executor.clone(), + pending_approvals: approvals.clone(), + workspace_id: "workspace-1".to_string(), + request_id: "request-1".to_string(), + session_id: "session-1".to_string(), + }; + + let approvals_for_task = approvals.clone(); + let handle = tokio::spawn(async move { + tool.call(RunTerminalCommandArgs { + command: "/buy AAPL 1".to_string(), + }) + .await + .expect("tool result") + }); + + tokio::task::yield_now().await; + approvals_for_task.resolve("approval-1", false).unwrap(); + + let result = handle.await.unwrap(); + assert!(result.contains("\"status\":\"denied\"")); + assert_eq!(executor.calls.load(Ordering::Relaxed), 0); + } + + #[tokio::test] + async fn approved_write_command_executes_once() { + let app = build_test_app(); + let approvals = Arc::new(PendingAgentToolApprovals::new()); + let executor = Arc::new(FakeExecutor::new(TerminalCommandResponse::Text { + content: "Bought 1 share".to_string(), + portfolio: None, + })); + + let tool = RunTerminalCommandTool { + app_handle: app.handle().clone(), + command_executor: executor.clone(), + pending_approvals: approvals.clone(), + workspace_id: "workspace-1".to_string(), + request_id: "request-1".to_string(), + session_id: "session-1".to_string(), + }; + + let approvals_for_task = approvals.clone(); + let handle = tokio::spawn(async move { + tool.call(RunTerminalCommandArgs { + command: "/buy AAPL 1".to_string(), + }) + .await + .expect("tool result") + }); + + tokio::task::yield_now().await; + approvals_for_task.resolve("approval-1", true).unwrap(); + + let result = handle.await.unwrap(); + assert!(result.contains("\"status\":\"executed\"")); + assert_eq!(executor.calls.load(Ordering::Relaxed), 1); + } + + fn build_test_app() -> tauri::App { + mock_builder() + .plugin(tauri_plugin_store::Builder::new().build()) + .build(mock_context(noop_assets())) + .unwrap() + } +} diff --git a/MosaicIQ/src-tauri/src/agent/types.rs b/MosaicIQ/src-tauri/src/agent/types.rs index 8b8e7cc..41fad6c 100644 --- a/MosaicIQ/src-tauri/src/agent/types.rs +++ b/MosaicIQ/src-tauri/src/agent/types.rs @@ -2,7 +2,7 @@ use rig::completion::Message; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use crate::terminal::PanelPayload; +use crate::terminal::{PanelPayload, TerminalCommandResponse}; /// Default Z.AI coding plan endpoint used by the app. pub const DEFAULT_REMOTE_BASE_URL: &str = "https://api.z.ai/api/coding/paas/v4"; @@ -89,6 +89,39 @@ pub struct AgentResultEvent { pub reply: String, } +/// Event emitted when the agent decides to run a terminal command tool. +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AgentToolCommandEvent { + pub workspace_id: String, + pub request_id: String, + pub session_id: String, + pub command: String, +} + +/// Event emitted after an agent-triggered command completes. +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AgentToolResultEvent { + pub workspace_id: String, + pub request_id: String, + pub session_id: String, + pub response: TerminalCommandResponse, +} + +/// Event emitted when the agent requests approval for a mutating command. +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AgentToolApprovalRequiredEvent { + pub workspace_id: String, + pub request_id: String, + pub session_id: String, + pub approval_id: String, + pub command: String, + pub title: String, + pub message: String, +} + /// Error event emitted when the backend cannot complete a stream. #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] @@ -99,6 +132,14 @@ pub struct AgentErrorEvent { pub message: String, } +/// Frontend request payload for approving or denying an agent-triggered write command. +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ResolveAgentToolApprovalRequest { + pub approval_id: String, + pub approved: bool, +} + /// Persisted settings for the remote chat provider. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] diff --git a/MosaicIQ/src-tauri/src/commands/terminal.rs b/MosaicIQ/src-tauri/src/commands/terminal.rs index 3d4e4b7..102fb97 100644 --- a/MosaicIQ/src-tauri/src/commands/terminal.rs +++ b/MosaicIQ/src-tauri/src/commands/terminal.rs @@ -4,8 +4,8 @@ use futures::StreamExt; use tauri::{Emitter, Manager}; use crate::agent::{ - AgentDeltaEvent, AgentErrorEvent, AgentResultEvent, ChatGateway, ChatPromptRequest, - ChatStreamStart, + AgentDeltaEvent, AgentErrorEvent, AgentResultEvent, AgentToolRuntimeContext, ChatGateway, + ChatPromptRequest, ChatStreamStart, ResolveAgentToolApprovalRequest, }; use crate::state::AppState; use crate::terminal::{ @@ -57,6 +57,8 @@ pub async fn start_chat_stream( }; let app_handle = app.clone(); + let command_executor = state.command_service.clone(); + let pending_approvals = state.pending_agent_tool_approvals.clone(); tauri::async_runtime::spawn(async move { tokio::time::sleep(Duration::from_millis(30)).await; @@ -68,6 +70,14 @@ pub async fn start_chat_stream( prepared_turn.prompt.clone(), prepared_turn.context_messages.clone(), prepared_turn.history.clone(), + AgentToolRuntimeContext { + app_handle: app_handle.clone(), + command_executor, + pending_approvals, + workspace_id: prepared_turn.workspace_id.clone(), + request_id: request_id.clone(), + session_id: prepared_turn.session_id.clone(), + }, ) .await { @@ -136,3 +146,15 @@ pub async fn start_chat_stream( Ok(start) } + +/// Resolves a pending agent-triggered command approval. +#[tauri::command] +pub async fn resolve_agent_tool_approval( + state: tauri::State<'_, AppState>, + request: ResolveAgentToolApprovalRequest, +) -> Result<(), String> { + state + .pending_agent_tool_approvals + .resolve(&request.approval_id, request.approved) + .map_err(|error| error.to_string()) +} diff --git a/MosaicIQ/src-tauri/src/error.rs b/MosaicIQ/src-tauri/src/error.rs index 9f97ecb..2936c41 100644 --- a/MosaicIQ/src-tauri/src/error.rs +++ b/MosaicIQ/src-tauri/src/error.rs @@ -11,6 +11,7 @@ pub enum AppError { RemoteApiKeyMissing, InvalidSettings(String), PanelContext(String), + AgentToolApprovalNotFound(String), UnknownSession(String), SettingsStore(String), ProviderInit(String), @@ -32,6 +33,9 @@ impl Display for AppError { Self::PanelContext(message) => { write!(formatter, "panel context could not be prepared: {message}") } + Self::AgentToolApprovalNotFound(approval_id) => { + write!(formatter, "unknown agent tool approval: {approval_id}") + } Self::UnknownSession(session_id) => { write!(formatter, "unknown session: {session_id}") } diff --git a/MosaicIQ/src-tauri/src/lib.rs b/MosaicIQ/src-tauri/src/lib.rs index e7a22d0..bd266ea 100644 --- a/MosaicIQ/src-tauri/src/lib.rs +++ b/MosaicIQ/src-tauri/src/lib.rs @@ -32,6 +32,7 @@ pub fn run() { commands::terminal::execute_terminal_command, commands::terminal::lookup_company, commands::terminal::start_chat_stream, + commands::terminal::resolve_agent_tool_approval, commands::settings::get_agent_config_status, commands::settings::save_agent_runtime_config, commands::settings::update_remote_api_key, diff --git a/MosaicIQ/src-tauri/src/state.rs b/MosaicIQ/src-tauri/src/state.rs index 9b0d595..18095d5 100644 --- a/MosaicIQ/src-tauri/src/state.rs +++ b/MosaicIQ/src-tauri/src/state.rs @@ -1,9 +1,11 @@ //! Shared application state managed by Tauri. +use std::collections::HashMap; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::{Arc, Mutex}; use tauri::{AppHandle, Wry}; +use tokio::sync::oneshot; use crate::agent::{AgentService, AgentSettingsService}; use crate::error::AppError; @@ -15,6 +17,59 @@ use crate::terminal::sec_edgar::{ use crate::terminal::security_lookup::SecurityLookup; use crate::terminal::TerminalCommandService; +pub struct PendingAgentToolApprovals { + next_approval_id: AtomicU64, + senders: Mutex>>, +} + +impl PendingAgentToolApprovals { + pub fn new() -> Self { + Self { + next_approval_id: AtomicU64::new(1), + senders: Mutex::new(HashMap::new()), + } + } + + pub fn register(&self) -> Result<(String, oneshot::Receiver), AppError> { + let approval_id = format!( + "approval-{}", + self.next_approval_id.fetch_add(1, Ordering::Relaxed) + ); + let (sender, receiver) = oneshot::channel(); + let mut senders = self + .senders + .lock() + .map_err(|_| AppError::InvalidSettings("approval state is unavailable".to_string()))?; + senders.insert(approval_id.clone(), sender); + Ok((approval_id, receiver)) + } + + pub fn resolve(&self, approval_id: &str, approved: bool) -> Result<(), AppError> { + let sender = self + .senders + .lock() + .map_err(|_| AppError::InvalidSettings("approval state is unavailable".to_string()))? + .remove(approval_id) + .ok_or_else(|| AppError::AgentToolApprovalNotFound(approval_id.to_string()))?; + + sender + .send(approved) + .map_err(|_| AppError::AgentToolApprovalNotFound(approval_id.to_string())) + } + + pub fn cancel(&self, approval_id: &str) { + if let Ok(mut senders) = self.senders.lock() { + senders.remove(approval_id); + } + } +} + +impl Default for PendingAgentToolApprovals { + fn default() -> Self { + Self::new() + } +} + struct SettingsBackedSecUserAgentProvider { settings: AgentSettingsService, } @@ -41,7 +96,9 @@ pub struct AppState { /// Stateful chat service used for per-session conversation history and agent config. pub agent: Mutex>, /// Slash-command executor backed by shared mock data. - pub command_service: TerminalCommandService, + pub command_service: Arc, + /// Pending approvals for agent-triggered mutating commands. + pub pending_agent_tool_approvals: Arc, next_request_id: AtomicU64, } @@ -60,11 +117,12 @@ impl AppState { Ok(Self { agent: Mutex::new(AgentService::new(app_handle)?), - command_service: TerminalCommandService::new( + command_service: Arc::new(TerminalCommandService::new( security_lookup, sec_edgar_lookup, portfolio_service, - ), + )), + pending_agent_tool_approvals: Arc::new(PendingAgentToolApprovals::new()), next_request_id: AtomicU64::new(1), }) } diff --git a/MosaicIQ/src/App.tsx b/MosaicIQ/src/App.tsx index ca996a5..d3e0f5b 100644 --- a/MosaicIQ/src/App.tsx +++ b/MosaicIQ/src/App.tsx @@ -4,6 +4,7 @@ import { CommandInputHandle } from './components/Terminal/CommandInput'; import { Sidebar } from './components/Sidebar/Sidebar'; import { TabBar } from './components/TabBar/TabBar'; import { SettingsPage } from './components/Settings/SettingsPage'; +import { ConfirmDialog } from './components/Settings/ConfirmDialog'; import { isPortfolioCommand, usePortfolioWorkflow, @@ -21,6 +22,7 @@ import { terminalBridge } from './lib/terminalBridge'; import { AgentConfigStatus } from './types/agentSettings'; import { Portfolio } from './types/financial'; import { + ResolvedTerminalCommandResponse, PortfolioAction, PortfolioActionDraft, PortfolioActionSeed, @@ -29,6 +31,15 @@ import './App.css'; type AppView = 'terminal' | 'settings'; +interface PendingAgentApproval { + approvalId: string; + command: string; + requestId: string; + workspaceId: string; + title: string; + message: string; +} + const isEditableTarget = (target: EventTarget | null) => { if (!(target instanceof HTMLElement)) { return false; @@ -50,6 +61,8 @@ function App() { const [isProcessing, setIsProcessing] = React.useState(false); const [agentStatus, setAgentStatus] = React.useState(null); const [activeView, setActiveView] = React.useState('terminal'); + const [pendingAgentApproval, setPendingAgentApproval] = + React.useState(null); const portfolioWorkflow = usePortfolioWorkflow(); const commandHistoryRefs = useRef>({}); const commandIndexRefs = useRef>({}); @@ -116,6 +129,33 @@ function App() { portfolioWorkflow.clearPortfolioAction(tabs.activeWorkspaceId); }, [portfolioWorkflow, tabs.activeWorkspaceId]); + const appendResolvedCommandResponse = useCallback( + ( + workspaceId: string, + command: string | undefined, + response: ResolvedTerminalCommandResponse, + ) => { + tabs.appendWorkspaceEntry( + workspaceId, + createEntry( + response.kind === 'text' + ? { type: 'response', content: response.content } + : { type: 'panel', content: response.panel }, + ), + ); + + if (command) { + portfolioWorkflow.noteCommandResponse(workspaceId, command, response); + } + + const tickerSymbol = extractTickerSymbolFromResponse(response); + if (tickerSymbol) { + void tickerHistory.recordTicker(tickerSymbol); + } + }, + [portfolioWorkflow, tabs, tickerHistory], + ); + const handleCommand = useCallback(async (command: string) => { const trimmedCommand = command.trim(); const latestTicker = tickerHistory.history[0]?.company.symbol; @@ -155,21 +195,7 @@ function App() { input: resolvedCommand, }); - tabs.appendWorkspaceEntry( - workspaceId, - createEntry( - response.kind === 'text' - ? { type: 'response', content: response.content } - : { type: 'panel', content: response.panel }, - ), - ); - - portfolioWorkflow.noteCommandResponse(workspaceId, resolvedCommand, response); - - const tickerSymbol = extractTickerSymbolFromResponse(response); - if (tickerSymbol) { - void tickerHistory.recordTicker(tickerSymbol); - } + appendResolvedCommandResponse(workspaceId, resolvedCommand, response); } catch (error) { tabs.appendWorkspaceEntry( workspaceId, @@ -189,7 +215,12 @@ function App() { // Plain text keeps the current workspace conversation alive and streams into a placeholder response entry. const panelContext = extractChatPanelContext(currentWorkspace?.history ?? []); const commandEntry = createEntry({ type: 'command', content: resolvedCommand }); - const responseEntry = createEntry({ type: 'response', content: '' }); + const responseEntry = createEntry({ + type: 'response', + content: '', + renderMode: 'markdown', + }); + const toolCommandQueue: string[] = []; tabs.appendWorkspaceEntry(workspaceId, commandEntry); tabs.appendWorkspaceEntry(workspaceId, responseEntry); @@ -200,7 +231,7 @@ function App() { workspaceId, sessionId: currentWorkspace?.chatSessionId, prompt: resolvedCommand, - agentProfile: 'interactiveChat', + agentProfile: 'toolUse', panelContext, }, { @@ -209,6 +240,7 @@ function App() { tabs.updateWorkspaceEntry(workspaceId, responseEntry.id, (entry) => ({ ...entry, content: typeof entry.content === 'string' ? `${entry.content}${event.delta}` : event.delta, + renderMode: 'markdown', timestamp: new Date(), })); }, @@ -218,6 +250,7 @@ function App() { ...entry, type: 'response', content: event.reply, + renderMode: 'markdown', timestamp: new Date(), })); setIsProcessing(false); @@ -227,10 +260,32 @@ function App() { ...entry, type: 'error', content: event.message, + renderMode: 'plain', timestamp: new Date(), })); setIsProcessing(false); }, + onToolCommand: (event) => { + toolCommandQueue.push(event.command); + tabs.appendWorkspaceEntry( + workspaceId, + createEntry({ type: 'command', content: event.command }), + ); + }, + onToolResult: (event) => { + const command = toolCommandQueue.shift(); + appendResolvedCommandResponse(workspaceId, command, event.response); + }, + onToolApprovalRequired: (event) => { + setPendingAgentApproval({ + approvalId: event.approvalId, + command: event.command, + requestId: event.requestId, + workspaceId: event.workspaceId, + title: event.title, + message: event.message, + }); + }, } ); @@ -240,6 +295,7 @@ function App() { ...entry, type: 'error', content: error instanceof Error ? error.message : 'Chat stream failed.', + renderMode: 'plain', timestamp: new Date(), })); setIsProcessing(false); @@ -250,6 +306,7 @@ function App() { pushCommandHistory, tickerHistory, portfolioWorkflow, + appendResolvedCommandResponse, ]); const runCommand = useCallback( @@ -443,6 +500,39 @@ function App() { /> )} + + { + if (!pendingAgentApproval) { + return; + } + + void terminalBridge + .resolveAgentToolApproval({ + approvalId: pendingAgentApproval.approvalId, + approved: true, + }) + .finally(() => setPendingAgentApproval(null)); + }} + onCancel={() => { + if (!pendingAgentApproval) { + return; + } + + void terminalBridge + .resolveAgentToolApproval({ + approvalId: pendingAgentApproval.approvalId, + approved: false, + }) + .finally(() => setPendingAgentApproval(null)); + }} + /> ); } diff --git a/MosaicIQ/src/components/Terminal/TerminalOutput.tsx b/MosaicIQ/src/components/Terminal/TerminalOutput.tsx index ab8d124..17f23f3 100644 --- a/MosaicIQ/src/components/Terminal/TerminalOutput.tsx +++ b/MosaicIQ/src/components/Terminal/TerminalOutput.tsx @@ -1,4 +1,6 @@ import React, { useEffect } from 'react'; +import ReactMarkdown from 'react-markdown'; +import remarkGfm from 'remark-gfm'; import { PanelPayload, PortfolioAction, @@ -38,19 +40,101 @@ export const TerminalOutput: React.FC = ({ } }, [history, outputRef]); + const renderPlainText = (content: string) => { + const lines = content.split('\n'); + return ( +
+ {lines.map((line, i) => ( +
{line || '\u00A0'}
+ ))} +
+ ); + }; + + const renderMarkdown = (content: string) => ( +
+ ( +

+ {children} +

+ ), + h2: ({ children }) => ( +

+ {children} +

+ ), + h3: ({ children }) => ( +

+ {children} +

+ ), + p: ({ children }) =>

{children}

, + ul: ({ children }) =>
    {children}
, + ol: ({ children }) =>
    {children}
, + li: ({ children }) =>
  • {children}
  • , + blockquote: ({ children }) => ( +
    + {children} +
    + ), + a: ({ href, children }) => ( + + {children} + + ), + hr: () =>
    , + table: ({ children }) => ( +
    + + {children} +
    +
    + ), + thead: ({ children }) => {children}, + tbody: ({ children }) => {children}, + tr: ({ children }) => {children}, + th: ({ children }) => {children}, + td: ({ children }) => {children}, + code: ({ className, children }) => { + const value = String(children).replace(/\n$/, ''); + const isBlock = Boolean(className?.includes('language-')) || value.includes('\n'); + + return isBlock ? ( + + {value} + + ) : ( + + {value} + + ); + }, + pre: ({ children }) =>
    {children}
    , + }} + > + {content} +
    +
    + ); + const renderContent = (entry: TerminalEntry) => { - if (typeof entry.content === 'string') { - const lines = entry.content.split('\n'); - return ( -
    - {lines.map((line, i) => ( -
    {line || '\u00A0'}
    - ))} -
    - ); + if (typeof entry.content !== 'string') { + return null; } - return null; + if (entry.renderMode === 'markdown') { + return renderMarkdown(entry.content); + } + + return renderPlainText(entry.content); }; const getEntryColor = (type: TerminalEntry['type']) => { diff --git a/MosaicIQ/src/lib/terminalBridge.ts b/MosaicIQ/src/lib/terminalBridge.ts index a4106c2..42b242d 100644 --- a/MosaicIQ/src/lib/terminalBridge.ts +++ b/MosaicIQ/src/lib/terminalBridge.ts @@ -5,13 +5,18 @@ import { AgentDeltaEvent, AgentErrorEvent, AgentResultEvent, + AgentToolApprovalRequiredEvent, + AgentToolCommandEvent, + AgentToolResultEvent, ChatStreamStart, LookupCompanyRequest, ExecuteTerminalCommandRequest, PanelPayload, + ResolveAgentToolApprovalRequest, ResolvedTerminalCommandResponse, StartChatStreamRequest, TerminalCommandResponse, + TransportAgentToolResultEvent, TransportPanelPayload, } from '../types/terminal'; import { Company } from '../types/financial'; @@ -21,6 +26,9 @@ interface StreamCallbacks { onDelta: (event: AgentDeltaEvent) => void; onResult: (event: AgentResultEvent) => void; onError: (event: AgentErrorEvent) => void; + onToolCommand: (event: AgentToolCommandEvent) => void; + onToolResult: (event: AgentToolResultEvent) => void; + onToolApprovalRequired: (event: AgentToolApprovalRequiredEvent) => void; } const deserializePanelPayload = (payload: TransportPanelPayload): PanelPayload => { @@ -40,6 +48,19 @@ const deserializePanelPayload = (payload: TransportPanelPayload): PanelPayload = }; }; +const deserializeTerminalCommandResponse = ( + response: TerminalCommandResponse, +): ResolvedTerminalCommandResponse => { + if (response.kind === 'text') { + return response; + } + + return { + kind: 'panel', + panel: deserializePanelPayload(response.panel), + }; +}; + class TerminalBridge { private listenersReady: Promise | null = null; private unlistenFns: UnlistenFn[] = []; @@ -75,6 +96,30 @@ class TerminalBridge { callbacks.onError(event.payload); this.streamCallbacks.delete(event.payload.requestId); }), + listen('agent_tool_command', (event) => { + const callbacks = this.streamCallbacks.get(event.payload.requestId); + if (!callbacks || callbacks.workspaceId !== event.payload.workspaceId) { + return; + } + callbacks.onToolCommand(event.payload); + }), + listen('agent_tool_result', (event) => { + const callbacks = this.streamCallbacks.get(event.payload.requestId); + if (!callbacks || callbacks.workspaceId !== event.payload.workspaceId) { + return; + } + callbacks.onToolResult({ + ...event.payload, + response: deserializeTerminalCommandResponse(event.payload.response), + }); + }), + listen('agent_tool_approval_required', (event) => { + const callbacks = this.streamCallbacks.get(event.payload.requestId); + if (!callbacks || callbacks.workspaceId !== event.payload.workspaceId) { + return; + } + callbacks.onToolApprovalRequired(event.payload); + }), ]).then((unlistenFns) => { this.unlistenFns = unlistenFns; }); @@ -93,10 +138,7 @@ class TerminalBridge { return response; } - return { - kind: 'panel', - panel: deserializePanelPayload(response.panel), - }; + return deserializeTerminalCommandResponse(response); } async lookupCompany(request: LookupCompanyRequest): Promise { @@ -124,6 +166,14 @@ class TerminalBridge { return start; } + async resolveAgentToolApproval( + request: ResolveAgentToolApprovalRequest, + ): Promise { + await invoke('resolve_agent_tool_approval', { + request, + }); + } + async dispose() { for (const unlisten of this.unlistenFns) { await unlisten(); diff --git a/MosaicIQ/src/types/terminal.ts b/MosaicIQ/src/types/terminal.ts index 1bf5064..7ad1b9d 100644 --- a/MosaicIQ/src/types/terminal.ts +++ b/MosaicIQ/src/types/terminal.ts @@ -70,6 +70,11 @@ export interface ChatStreamStart { sessionId: string; } +export interface ResolveAgentToolApprovalRequest { + approvalId: string; + approved: boolean; +} + export interface AgentDeltaEvent { workspaceId: string; requestId: string; @@ -91,10 +96,42 @@ export interface AgentErrorEvent { message: string; } +export interface AgentToolCommandEvent { + workspaceId: string; + requestId: string; + sessionId: string; + command: string; +} + +export interface TransportAgentToolResultEvent { + workspaceId: string; + requestId: string; + sessionId: string; + response: TerminalCommandResponse; +} + +export interface AgentToolResultEvent { + workspaceId: string; + requestId: string; + sessionId: string; + response: ResolvedTerminalCommandResponse; +} + +export interface AgentToolApprovalRequiredEvent { + workspaceId: string; + requestId: string; + sessionId: string; + approvalId: string; + command: string; + title: string; + message: string; +} + export interface TerminalEntry { id: string; type: 'command' | 'response' | 'system' | 'error' | 'panel'; content: string | PanelPayload; + renderMode?: 'plain' | 'markdown'; timestamp?: Date; }