diff --git a/README.md b/README.md index fce7a954..8f0b40bc 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,34 @@ This is the source code repository for the documentation of [image-js](https://g The documentation is available on [https://image-js-docs.pages.dev/](https://image-js-docs.pages.dev/) +## Create demos +A demo is simply function which takes an image as input and returns an image as output. When imported in `md` files, it will be transformed into a demo component which allows to choose from various image or video sources to showcase the image transformation. + +### Example + +In `docs/demos/gaussian-blur.demo.tsx`, define your demo function. + +```ts +import { Image } from 'image-js'; + +export default function blur(image: Image) { + return image.gaussianBlur({ sigma: 2 }); +} +``` + +Then in `docs/blur-filters.md`, import and use the demo component. + +```markdown +import GaussianBlur from './demos/gaussian-blur.demo.tsx'; + +# Gaussian blur + + +``` + +### Caveats + +1. The file must end with `.demo.tsx` to get processed correctly by the builder. The file extension should be `.tsx`, even if the file does not render any JSX. +2. The file must export a default function, which takes an `Image` as input and returns an `Image` as output. +3. The demo must only import from `image-js`. diff --git a/demo-loader.webpack.cjs b/demo-loader.webpack.cjs index 85370862..0a48b994 100644 --- a/demo-loader.webpack.cjs +++ b/demo-loader.webpack.cjs @@ -65,7 +65,7 @@ export function process(image) { let imageDemoImportPath = path .relative( this.context, - path.join('src', 'components', 'demo', 'ImageDemo.tsx'), + path.join('src', 'demo', 'components', 'ImageDemo.tsx'), ) .replaceAll(path.sep, path.posix.sep); @@ -81,11 +81,12 @@ export function process(image) { function process(image, IJS) { ${processBody} } - const code= \`${source}\`; + const name = '${this.resourcePath}'; + const code = \`${source}\`; const defaultEditorCode= \`${editorCode}\`; - export default function Demo() { + export default function Demo(props) { return ( - + ); } `; diff --git a/docs/filters/intro.md b/docs/filters/intro.md index fc8383bb..571c9f6e 100644 --- a/docs/filters/intro.md +++ b/docs/filters/intro.md @@ -13,4 +13,4 @@ import BlurDemo from './blur.demo.tsx' ## Blur - + diff --git a/package-lock.json b/package-lock.json index d6320a11..40923ff0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,8 @@ "name": "image-js-docs", "version": "0.0.0", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/preset-classic": "2.3.1", + "@docusaurus/core": "^2.4.0", + "@docusaurus/preset-classic": "^2.4.0", "@mdx-js/react": "^1.6.22", "@monaco-editor/react": "^4.5.0", "clsx": "^1.2.1", @@ -23,7 +23,7 @@ "remove": "^0.1.5" }, "devDependencies": { - "@docusaurus/module-type-aliases": "2.3.1", + "@docusaurus/module-type-aliases": "^2.4.0", "@tsconfig/docusaurus": "^1.0.5", "dts-bundle": "^0.7.3", "eslint": "^8.38.0", @@ -36,19 +36,19 @@ } }, "node_modules/@algolia/autocomplete-core": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.7.4.tgz", - "integrity": "sha512-daoLpQ3ps/VTMRZDEBfU8ixXd+amZcNJ4QSP3IERGyzqnL5Ch8uSRFt/4G8pUvW9c3o6GA4vtVv4I4lmnkdXyg==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.8.2.tgz", + "integrity": "sha512-mTeshsyFhAqw/ebqNsQpMtbnjr+qVOSKXArEj4K0d7sqc8It1XD0gkASwecm9mF/jlOQ4Z9RNg1HbdA8JPdRwQ==", "dependencies": { - "@algolia/autocomplete-shared": "1.7.4" + "@algolia/autocomplete-shared": "1.8.2" } }, "node_modules/@algolia/autocomplete-preset-algolia": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.7.4.tgz", - "integrity": "sha512-s37hrvLEIfcmKY8VU9LsAXgm2yfmkdHT3DnA3SgHaY93yjZ2qL57wzb5QweVkYuEBZkT2PIREvRoLXC2sxTbpQ==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.8.2.tgz", + "integrity": "sha512-J0oTx4me6ZM9kIKPuL3lyU3aB8DEvpVvR6xWmHVROx5rOYJGQcZsdG4ozxwcOyiiu3qxMkIbzntnV1S1VWD8yA==", "dependencies": { - "@algolia/autocomplete-shared": "1.7.4" + "@algolia/autocomplete-shared": "1.8.2" }, "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", @@ -56,79 +56,79 @@ } }, "node_modules/@algolia/autocomplete-shared": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.7.4.tgz", - "integrity": "sha512-2VGCk7I9tA9Ge73Km99+Qg87w0wzW4tgUruvWAn/gfey1ZXgmxZtyIRBebk35R1O8TbK77wujVtCnpsGpRy1kg==" + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.8.2.tgz", + "integrity": "sha512-b6Z/X4MczChMcfhk6kfRmBzPgjoPzuS9KGR4AFsiLulLNRAAqhP+xZTKtMnZGhLuc61I20d5WqlId02AZvcO6g==" }, "node_modules/@algolia/cache-browser-local-storage": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.14.3.tgz", - "integrity": "sha512-hWH1yCxgG3+R/xZIscmUrWAIBnmBFHH5j30fY/+aPkEZWt90wYILfAHIOZ1/Wxhho5SkPfwFmT7ooX2d9JeQBw==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.17.0.tgz", + "integrity": "sha512-myRSRZDIMYB8uCkO+lb40YKiYHi0fjpWRtJpR/dgkaiBlSD0plRyB6lLOh1XIfmMcSeBOqDE7y9m8xZMrXYfyQ==", "dependencies": { - "@algolia/cache-common": "4.14.3" + "@algolia/cache-common": "4.17.0" } }, "node_modules/@algolia/cache-common": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.14.3.tgz", - "integrity": "sha512-oZJofOoD9FQOwiGTzyRnmzvh3ZP8WVTNPBLH5xU5JNF7drDbRT0ocVT0h/xB2rPHYzOeXRrLaQQBwRT/CKom0Q==" + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.17.0.tgz", + "integrity": "sha512-g8mXzkrcUBIPZaulAuqE7xyHhLAYAcF2xSch7d9dABheybaU3U91LjBX6eJTEB7XVhEsgK4Smi27vWtAJRhIKQ==" }, "node_modules/@algolia/cache-in-memory": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.14.3.tgz", - "integrity": "sha512-ES0hHQnzWjeioLQf5Nq+x1AWdZJ50znNPSH3puB/Y4Xsg4Av1bvLmTJe7SY2uqONaeMTvL0OaVcoVtQgJVw0vg==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.17.0.tgz", + "integrity": "sha512-PT32ciC/xI8z919d0oknWVu3kMfTlhQn3MKxDln3pkn+yA7F7xrxSALysxquv+MhFfNAcrtQ/oVvQVBAQSHtdw==", "dependencies": { - "@algolia/cache-common": "4.14.3" + "@algolia/cache-common": "4.17.0" } }, "node_modules/@algolia/client-account": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.14.3.tgz", - "integrity": "sha512-PBcPb0+f5Xbh5UfLZNx2Ow589OdP8WYjB4CnvupfYBrl9JyC1sdH4jcq/ri8osO/mCZYjZrQsKAPIqW/gQmizQ==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.17.0.tgz", + "integrity": "sha512-sSEHx9GA6m7wrlsSMNBGfyzlIfDT2fkz2u7jqfCCd6JEEwmxt8emGmxAU/0qBfbhRSuGvzojoLJlr83BSZAKjA==", "dependencies": { - "@algolia/client-common": "4.14.3", - "@algolia/client-search": "4.14.3", - "@algolia/transporter": "4.14.3" + "@algolia/client-common": "4.17.0", + "@algolia/client-search": "4.17.0", + "@algolia/transporter": "4.17.0" } }, "node_modules/@algolia/client-analytics": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.14.3.tgz", - "integrity": "sha512-eAwQq0Hb/aauv9NhCH5Dp3Nm29oFx28sayFN2fdOWemwSeJHIl7TmcsxVlRsO50fsD8CtPcDhtGeD3AIFLNvqw==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.17.0.tgz", + "integrity": "sha512-84ooP8QA3mQ958hQ9wozk7hFUbAO+81CX1CjAuerxBqjKIInh1fOhXKTaku05O/GHBvcfExpPLIQuSuLYziBXQ==", "dependencies": { - "@algolia/client-common": "4.14.3", - "@algolia/client-search": "4.14.3", - "@algolia/requester-common": "4.14.3", - "@algolia/transporter": "4.14.3" + "@algolia/client-common": "4.17.0", + "@algolia/client-search": "4.17.0", + "@algolia/requester-common": "4.17.0", + "@algolia/transporter": "4.17.0" } }, "node_modules/@algolia/client-common": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.14.3.tgz", - "integrity": "sha512-jkPPDZdi63IK64Yg4WccdCsAP4pHxSkr4usplkUZM5C1l1oEpZXsy2c579LQ0rvwCs5JFmwfNG4ahOszidfWPw==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.17.0.tgz", + "integrity": "sha512-jHMks0ZFicf8nRDn6ma8DNNsdwGgP/NKiAAL9z6rS7CymJ7L0+QqTJl3rYxRW7TmBhsUH40wqzmrG6aMIN/DrQ==", "dependencies": { - "@algolia/requester-common": "4.14.3", - "@algolia/transporter": "4.14.3" + "@algolia/requester-common": "4.17.0", + "@algolia/transporter": "4.17.0" } }, "node_modules/@algolia/client-personalization": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.14.3.tgz", - "integrity": "sha512-UCX1MtkVNgaOL9f0e22x6tC9e2H3unZQlSUdnVaSKpZ+hdSChXGaRjp2UIT7pxmPqNCyv51F597KEX5WT60jNg==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.17.0.tgz", + "integrity": "sha512-RMzN4dZLIta1YuwT7QC9o+OeGz2cU6eTOlGNE/6RcUBLOU3l9tkCOdln5dPE2jp8GZXPl2yk54b2nSs1+pAjqw==", "dependencies": { - "@algolia/client-common": "4.14.3", - "@algolia/requester-common": "4.14.3", - "@algolia/transporter": "4.14.3" + "@algolia/client-common": "4.17.0", + "@algolia/requester-common": "4.17.0", + "@algolia/transporter": "4.17.0" } }, "node_modules/@algolia/client-search": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.14.3.tgz", - "integrity": "sha512-I2U7xBx5OPFdPLA8AXKUPPxGY3HDxZ4r7+mlZ8ZpLbI8/ri6fnu6B4z3wcL7sgHhDYMwnAE8Xr0AB0h3Hnkp4A==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.17.0.tgz", + "integrity": "sha512-x4P2wKrrRIXszT8gb7eWsMHNNHAJs0wE7/uqbufm4tZenAp+hwU/hq5KVsY50v+PfwM0LcDwwn/1DroujsTFoA==", "dependencies": { - "@algolia/client-common": "4.14.3", - "@algolia/requester-common": "4.14.3", - "@algolia/transporter": "4.14.3" + "@algolia/client-common": "4.17.0", + "@algolia/requester-common": "4.17.0", + "@algolia/transporter": "4.17.0" } }, "node_modules/@algolia/events": { @@ -137,47 +137,47 @@ "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" }, "node_modules/@algolia/logger-common": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.14.3.tgz", - "integrity": "sha512-kUEAZaBt/J3RjYi8MEBT2QEexJR2kAE2mtLmezsmqMQZTV502TkHCxYzTwY2dE7OKcUTxi4OFlMuS4GId9CWPw==" + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.17.0.tgz", + "integrity": "sha512-DGuoZqpTmIKJFDeyAJ7M8E/LOenIjWiOsg1XJ1OqAU/eofp49JfqXxbfgctlVZVmDABIyOz8LqEoJ6ZP4DTyvw==" }, "node_modules/@algolia/logger-console": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.14.3.tgz", - "integrity": "sha512-ZWqAlUITktiMN2EiFpQIFCJS10N96A++yrexqC2Z+3hgF/JcKrOxOdT4nSCQoEPvU4Ki9QKbpzbebRDemZt/hw==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.17.0.tgz", + "integrity": "sha512-zMPvugQV/gbXUvWBCzihw6m7oxIKp48w37QBIUu/XqQQfxhjoOE9xyfJr1KldUt5FrYOKZJVsJaEjTsu+bIgQg==", "dependencies": { - "@algolia/logger-common": "4.14.3" + "@algolia/logger-common": "4.17.0" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.14.3.tgz", - "integrity": "sha512-AZeg2T08WLUPvDncl2XLX2O67W5wIO8MNaT7z5ii5LgBTuk/rU4CikTjCe2xsUleIZeFl++QrPAi4Bdxws6r/Q==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.17.0.tgz", + "integrity": "sha512-aSOX/smauyTkP21Pf52pJ1O2LmNFJ5iHRIzEeTh0mwBeADO4GdG94cAWDILFA9rNblq/nK3EDh3+UyHHjplZ1A==", "dependencies": { - "@algolia/requester-common": "4.14.3" + "@algolia/requester-common": "4.17.0" } }, "node_modules/@algolia/requester-common": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.14.3.tgz", - "integrity": "sha512-RrRzqNyKFDP7IkTuV3XvYGF9cDPn9h6qEDl595lXva3YUk9YSS8+MGZnnkOMHvjkrSCKfoLeLbm/T4tmoIeclw==" + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.17.0.tgz", + "integrity": "sha512-XJjmWFEUlHu0ijvcHBoixuXfEoiRUdyzQM6YwTuB8usJNIgShua8ouFlRWF8iCeag0vZZiUm4S2WCVBPkdxFgg==" }, "node_modules/@algolia/requester-node-http": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.14.3.tgz", - "integrity": "sha512-O5wnPxtDRPuW2U0EaOz9rMMWdlhwP0J0eSL1Z7TtXF8xnUeeUyNJrdhV5uy2CAp6RbhM1VuC3sOJcIR6Av+vbA==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.17.0.tgz", + "integrity": "sha512-bpb/wDA1aC6WxxM8v7TsFspB7yBN3nqCGs2H1OADolQR/hiAIjAxusbuMxVbRFOdaUvAIqioIIkWvZdpYNIn8w==", "dependencies": { - "@algolia/requester-common": "4.14.3" + "@algolia/requester-common": "4.17.0" } }, "node_modules/@algolia/transporter": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.14.3.tgz", - "integrity": "sha512-2qlKlKsnGJ008exFRb5RTeTOqhLZj0bkMCMVskxoqWejs2Q2QtWmsiH98hDfpw0fmnyhzHEt0Z7lqxBYp8bW2w==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.17.0.tgz", + "integrity": "sha512-6xL6H6fe+Fi0AEP3ziSgC+G04RK37iRb4uUUqVAH9WPYFI8g+LYFq6iv5HS8Cbuc5TTut+Bwj6G+dh/asdb9uA==", "dependencies": { - "@algolia/cache-common": "4.14.3", - "@algolia/logger-common": "4.14.3", - "@algolia/requester-common": "4.14.3" + "@algolia/cache-common": "4.17.0", + "@algolia/logger-common": "4.17.0", + "@algolia/requester-common": "4.17.0" } }, "node_modules/@ampproject/remapping": { @@ -1990,18 +1990,18 @@ } }, "node_modules/@docsearch/css": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.3.3.tgz", - "integrity": "sha512-6SCwI7P8ao+se1TUsdZ7B4XzL+gqeQZnBc+2EONZlcVa0dVrk0NjETxozFKgMv0eEGH8QzP1fkN+A1rH61l4eg==" + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.3.4.tgz", + "integrity": "sha512-vDwCDoVXDgopw/hvr0zEADew2wWaGP8Qq0Bxhgii1Ewz2t4fQeyJwIRN/mWADeLFYPVkpz8TpEbxya/i6Tm0WA==" }, "node_modules/@docsearch/react": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.3.3.tgz", - "integrity": "sha512-pLa0cxnl+G0FuIDuYlW+EBK6Rw2jwLw9B1RHIeS4N4s2VhsfJ/wzeCi3CWcs5yVfxLd5ZK50t//TMA5e79YT7Q==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.3.4.tgz", + "integrity": "sha512-aeOf1WC5zMzBEi2SI6WWznOmIo9rnpN4p7a3zHXxowVciqlI4HsZGtOR9nFOufLeolv7HibwLlaM0oyUqJxasw==", "dependencies": { - "@algolia/autocomplete-core": "1.7.4", - "@algolia/autocomplete-preset-algolia": "1.7.4", - "@docsearch/css": "3.3.3", + "@algolia/autocomplete-core": "1.8.2", + "@algolia/autocomplete-preset-algolia": "1.8.2", + "@docsearch/css": "3.3.4", "algoliasearch": "^4.0.0" }, "peerDependencies": { @@ -2022,9 +2022,9 @@ } }, "node_modules/@docusaurus/core": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-2.3.1.tgz", - "integrity": "sha512-0Jd4jtizqnRAr7svWaBbbrCCN8mzBNd2xFLoT/IM7bGfFie5y58oz97KzXliwiLY3zWjqMXjQcuP1a5VgCv2JA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-2.4.0.tgz", + "integrity": "sha512-J55/WEoIpRcLf3afO5POHPguVZosKmJEQWKBL+K7TAnfuE7i+Y0NPLlkKtnWCehagGsgTqClfQEexH/UT4kELA==", "dependencies": { "@babel/core": "^7.18.6", "@babel/generator": "^7.18.7", @@ -2036,13 +2036,13 @@ "@babel/runtime": "^7.18.6", "@babel/runtime-corejs3": "^7.18.6", "@babel/traverse": "^7.18.8", - "@docusaurus/cssnano-preset": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", + "@docusaurus/cssnano-preset": "2.4.0", + "@docusaurus/logger": "2.4.0", + "@docusaurus/mdx-loader": "2.4.0", "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-common": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-common": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "@slorber/static-site-generator-webpack-plugin": "^4.0.7", "@svgr/webpack": "^6.2.1", "autoprefixer": "^10.4.7", @@ -2110,9 +2110,9 @@ } }, "node_modules/@docusaurus/cssnano-preset": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.3.1.tgz", - "integrity": "sha512-7mIhAROES6CY1GmCjR4CZkUfjTL6B3u6rKHK0ChQl2d1IevYXq/k/vFgvOrJfcKxiObpMnE9+X6R2Wt1KqxC6w==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-2.4.0.tgz", + "integrity": "sha512-RmdiA3IpsLgZGXRzqnmTbGv43W4OD44PCo+6Q/aYjEM2V57vKCVqNzuafE94jv0z/PjHoXUrjr69SaRymBKYYw==", "dependencies": { "cssnano-preset-advanced": "^5.3.8", "postcss": "^8.4.14", @@ -2124,9 +2124,9 @@ } }, "node_modules/@docusaurus/logger": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-2.3.1.tgz", - "integrity": "sha512-2lAV/olKKVr9qJhfHFCaqBIl8FgYjbUFwgUnX76+cULwQYss+42ZQ3grHGFvI0ocN2X55WcYe64ellQXz7suqg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-2.4.0.tgz", + "integrity": "sha512-T8+qR4APN+MjcC9yL2Es+xPJ2923S9hpzDmMtdsOcUGLqpCGBbU1vp3AAqDwXtVgFkq+NsEk7sHdVsfLWR/AXw==", "dependencies": { "chalk": "^4.1.2", "tslib": "^2.4.0" @@ -2136,14 +2136,14 @@ } }, "node_modules/@docusaurus/mdx-loader": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.3.1.tgz", - "integrity": "sha512-Gzga7OsxQRpt3392K9lv/bW4jGppdLFJh3luKRknCKSAaZrmVkOQv2gvCn8LAOSZ3uRg5No7AgYs/vpL8K94lA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-2.4.0.tgz", + "integrity": "sha512-GWoH4izZKOmFoC+gbI2/y8deH/xKLvzz/T5BsEexBye8EHQlwsA7FMrVa48N063bJBH4FUOiRRXxk5rq9cC36g==", "dependencies": { "@babel/parser": "^7.18.8", "@babel/traverse": "^7.18.8", - "@docusaurus/logger": "2.3.1", - "@docusaurus/utils": "2.3.1", + "@docusaurus/logger": "2.4.0", + "@docusaurus/utils": "2.4.0", "@mdx-js/mdx": "^1.6.22", "escape-html": "^1.0.3", "file-loader": "^6.2.0", @@ -2167,12 +2167,12 @@ } }, "node_modules/@docusaurus/module-type-aliases": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-2.3.1.tgz", - "integrity": "sha512-6KkxfAVOJqIUynTRb/tphYCl+co3cP0PlHiMDbi+SzmYxMdgIrwYqH9yAnGSDoN6Jk2ZE/JY/Azs/8LPgKP48A==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-2.4.0.tgz", + "integrity": "sha512-YEQO2D3UXs72qCn8Cr+RlycSQXVGN9iEUyuHwTuK4/uL/HFomB2FHSU0vSDM23oLd+X/KibQ3Ez6nGjQLqXcHg==", "dependencies": { "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/types": "2.3.1", + "@docusaurus/types": "2.4.0", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2186,17 +2186,17 @@ } }, "node_modules/@docusaurus/plugin-content-blog": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.3.1.tgz", - "integrity": "sha512-f5LjqX+9WkiLyGiQ41x/KGSJ/9bOjSD8lsVhPvYeUYHCtYpuiDKfhZE07O4EqpHkBx4NQdtQDbp+aptgHSTuiw==", - "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-common": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.4.0.tgz", + "integrity": "sha512-YwkAkVUxtxoBAIj/MCb4ohN0SCtHBs4AS75jMhPpf67qf3j+U/4n33cELq7567hwyZ6fMz2GPJcVmctzlGGThQ==", + "dependencies": { + "@docusaurus/core": "2.4.0", + "@docusaurus/logger": "2.4.0", + "@docusaurus/mdx-loader": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-common": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "cheerio": "^1.0.0-rc.12", "feed": "^4.2.2", "fs-extra": "^10.1.0", @@ -2216,17 +2216,17 @@ } }, "node_modules/@docusaurus/plugin-content-docs": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.3.1.tgz", - "integrity": "sha512-DxztTOBEruv7qFxqUtbsqXeNcHqcVEIEe+NQoI1oi2DBmKBhW/o0MIal8lt+9gvmpx3oYtlwmLOOGepxZgJGkw==", - "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/module-type-aliases": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.4.0.tgz", + "integrity": "sha512-ic/Z/ZN5Rk/RQo+Io6rUGpToOtNbtPloMR2JcGwC1xT2riMu6zzfSwmBi9tHJgdXH6CB5jG+0dOZZO8QS5tmDg==", + "dependencies": { + "@docusaurus/core": "2.4.0", + "@docusaurus/logger": "2.4.0", + "@docusaurus/mdx-loader": "2.4.0", + "@docusaurus/module-type-aliases": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "@types/react-router-config": "^5.0.6", "combine-promises": "^1.1.0", "fs-extra": "^10.1.0", @@ -2246,15 +2246,15 @@ } }, "node_modules/@docusaurus/plugin-content-pages": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.3.1.tgz", - "integrity": "sha512-E80UL6hvKm5VVw8Ka8YaVDtO6kWWDVUK4fffGvkpQ/AJQDOg99LwOXKujPoICC22nUFTsZ2Hp70XvpezCsFQaA==", - "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.4.0.tgz", + "integrity": "sha512-Pk2pOeOxk8MeU3mrTU0XLIgP9NZixbdcJmJ7RUFrZp1Aj42nd0RhIT14BGvXXyqb8yTQlk4DmYGAzqOfBsFyGw==", + "dependencies": { + "@docusaurus/core": "2.4.0", + "@docusaurus/mdx-loader": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "fs-extra": "^10.1.0", "tslib": "^2.4.0", "webpack": "^5.73.0" @@ -2268,13 +2268,13 @@ } }, "node_modules/@docusaurus/plugin-debug": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-2.3.1.tgz", - "integrity": "sha512-Ujpml1Ppg4geB/2hyu2diWnO49az9U2bxM9Shen7b6qVcyFisNJTkVG2ocvLC7wM1efTJcUhBO6zAku2vKJGMw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-2.4.0.tgz", + "integrity": "sha512-KC56DdYjYT7Txyux71vXHXGYZuP6yYtqwClvYpjKreWIHWus5Zt6VNi23rMZv3/QKhOCrN64zplUbdfQMvddBQ==", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils": "2.4.0", "fs-extra": "^10.1.0", "react-json-view": "^1.21.3", "tslib": "^2.4.0" @@ -2288,13 +2288,13 @@ } }, "node_modules/@docusaurus/plugin-google-analytics": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.3.1.tgz", - "integrity": "sha512-OHip0GQxKOFU8n7gkt3TM4HOYTXPCFDjqKbMClDD3KaDnyTuMp/Zvd9HSr770lLEscgPWIvzhJByRAClqsUWiQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.4.0.tgz", + "integrity": "sha512-uGUzX67DOAIglygdNrmMOvEp8qG03X20jMWadeqVQktS6nADvozpSLGx4J0xbkblhJkUzN21WiilsP9iVP+zkw==", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "tslib": "^2.4.0" }, "engines": { @@ -2306,13 +2306,13 @@ } }, "node_modules/@docusaurus/plugin-google-gtag": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.3.1.tgz", - "integrity": "sha512-uXtDhfu4+Hm+oqWUySr3DNI5cWC/rmP6XJyAk83Heor3dFjZqDwCbkX8yWPywkRiWev3Dk/rVF8lEn0vIGVocA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.4.0.tgz", + "integrity": "sha512-adj/70DANaQs2+TF/nRdMezDXFAV/O/pjAbUgmKBlyOTq5qoMe0Tk4muvQIwWUmiUQxFJe+sKlZGM771ownyOg==", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "tslib": "^2.4.0" }, "engines": { @@ -2324,13 +2324,13 @@ } }, "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-2.3.1.tgz", - "integrity": "sha512-Ww2BPEYSqg8q8tJdLYPFFM3FMDBCVhEM4UUqKzJaiRMx3NEoly3qqDRAoRDGdIhlC//Rf0iJV9cWAoq2m6k3sw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-2.4.0.tgz", + "integrity": "sha512-E66uGcYs4l7yitmp/8kMEVQftFPwV9iC62ORh47Veqzs6ExwnhzBkJmwDnwIysHBF1vlxnzET0Fl2LfL5fRR3A==", "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "tslib": "^2.4.0" }, "engines": { @@ -2342,16 +2342,16 @@ } }, "node_modules/@docusaurus/plugin-sitemap": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.3.1.tgz", - "integrity": "sha512-8Yxile/v6QGYV9vgFiYL+8d2N4z4Er3pSHsrD08c5XI8bUXxTppMwjarDUTH/TRTfgAWotRbhJ6WZLyajLpozA==", - "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-common": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.4.0.tgz", + "integrity": "sha512-pZxh+ygfnI657sN8a/FkYVIAmVv0CGk71QMKqJBOfMmDHNN1FeDeFkBjWP49ejBqpqAhjufkv5UWq3UOu2soCw==", + "dependencies": { + "@docusaurus/core": "2.4.0", + "@docusaurus/logger": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-common": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "fs-extra": "^10.1.0", "sitemap": "^7.1.1", "tslib": "^2.4.0" @@ -2365,23 +2365,23 @@ } }, "node_modules/@docusaurus/preset-classic": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-2.3.1.tgz", - "integrity": "sha512-OQ5W0AHyfdUk0IldwJ3BlnZ1EqoJuu2L2BMhqLbqwNWdkmzmSUvlFLH1Pe7CZSQgB2YUUC/DnmjbPKk/qQD0lQ==", - "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/plugin-content-blog": "2.3.1", - "@docusaurus/plugin-content-docs": "2.3.1", - "@docusaurus/plugin-content-pages": "2.3.1", - "@docusaurus/plugin-debug": "2.3.1", - "@docusaurus/plugin-google-analytics": "2.3.1", - "@docusaurus/plugin-google-gtag": "2.3.1", - "@docusaurus/plugin-google-tag-manager": "2.3.1", - "@docusaurus/plugin-sitemap": "2.3.1", - "@docusaurus/theme-classic": "2.3.1", - "@docusaurus/theme-common": "2.3.1", - "@docusaurus/theme-search-algolia": "2.3.1", - "@docusaurus/types": "2.3.1" + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-2.4.0.tgz", + "integrity": "sha512-/5z5o/9bc6+P5ool2y01PbJhoGddEGsC0ej1MF6mCoazk8A+kW4feoUd68l7Bnv01rCnG3xy7kHUQP97Y0grUA==", + "dependencies": { + "@docusaurus/core": "2.4.0", + "@docusaurus/plugin-content-blog": "2.4.0", + "@docusaurus/plugin-content-docs": "2.4.0", + "@docusaurus/plugin-content-pages": "2.4.0", + "@docusaurus/plugin-debug": "2.4.0", + "@docusaurus/plugin-google-analytics": "2.4.0", + "@docusaurus/plugin-google-gtag": "2.4.0", + "@docusaurus/plugin-google-tag-manager": "2.4.0", + "@docusaurus/plugin-sitemap": "2.4.0", + "@docusaurus/theme-classic": "2.4.0", + "@docusaurus/theme-common": "2.4.0", + "@docusaurus/theme-search-algolia": "2.4.0", + "@docusaurus/types": "2.4.0" }, "engines": { "node": ">=16.14" @@ -2404,26 +2404,26 @@ } }, "node_modules/@docusaurus/theme-classic": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-2.3.1.tgz", - "integrity": "sha512-SelSIDvyttb7ZYHj8vEUhqykhAqfOPKk+uP0z85jH72IMC58e7O8DIlcAeBv+CWsLbNIl9/Hcg71X0jazuxJug==", - "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/module-type-aliases": "2.3.1", - "@docusaurus/plugin-content-blog": "2.3.1", - "@docusaurus/plugin-content-docs": "2.3.1", - "@docusaurus/plugin-content-pages": "2.3.1", - "@docusaurus/theme-common": "2.3.1", - "@docusaurus/theme-translations": "2.3.1", - "@docusaurus/types": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-common": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-2.4.0.tgz", + "integrity": "sha512-GMDX5WU6Z0OC65eQFgl3iNNEbI9IMJz9f6KnOyuMxNUR6q0qVLsKCNopFUDfFNJ55UU50o7P7o21yVhkwpfJ9w==", + "dependencies": { + "@docusaurus/core": "2.4.0", + "@docusaurus/mdx-loader": "2.4.0", + "@docusaurus/module-type-aliases": "2.4.0", + "@docusaurus/plugin-content-blog": "2.4.0", + "@docusaurus/plugin-content-docs": "2.4.0", + "@docusaurus/plugin-content-pages": "2.4.0", + "@docusaurus/theme-common": "2.4.0", + "@docusaurus/theme-translations": "2.4.0", + "@docusaurus/types": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-common": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "@mdx-js/react": "^1.6.22", "clsx": "^1.2.1", "copy-text-to-clipboard": "^3.0.1", - "infima": "0.2.0-alpha.42", + "infima": "0.2.0-alpha.43", "lodash": "^4.17.21", "nprogress": "^0.2.0", "postcss": "^8.4.14", @@ -2443,16 +2443,17 @@ } }, "node_modules/@docusaurus/theme-common": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.3.1.tgz", - "integrity": "sha512-RYmYl2OR2biO+yhmW1aS5FyEvnrItPINa+0U2dMxcHpah8reSCjQ9eJGRmAgkZFchV1+aIQzXOI1K7LCW38O0g==", - "dependencies": { - "@docusaurus/mdx-loader": "2.3.1", - "@docusaurus/module-type-aliases": "2.3.1", - "@docusaurus/plugin-content-blog": "2.3.1", - "@docusaurus/plugin-content-docs": "2.3.1", - "@docusaurus/plugin-content-pages": "2.3.1", - "@docusaurus/utils": "2.3.1", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-2.4.0.tgz", + "integrity": "sha512-IkG/l5f/FLY6cBIxtPmFnxpuPzc5TupuqlOx+XDN+035MdQcAh8wHXXZJAkTeYDeZ3anIUSUIvWa7/nRKoQEfg==", + "dependencies": { + "@docusaurus/mdx-loader": "2.4.0", + "@docusaurus/module-type-aliases": "2.4.0", + "@docusaurus/plugin-content-blog": "2.4.0", + "@docusaurus/plugin-content-docs": "2.4.0", + "@docusaurus/plugin-content-pages": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-common": "2.4.0", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2472,18 +2473,18 @@ } }, "node_modules/@docusaurus/theme-search-algolia": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.3.1.tgz", - "integrity": "sha512-JdHaRqRuH1X++g5fEMLnq7OtULSGQdrs9AbhcWRQ428ZB8/HOiaN6mj3hzHvcD3DFgu7koIVtWPQnvnN7iwzHA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.4.0.tgz", + "integrity": "sha512-pPCJSCL1Qt4pu/Z0uxBAuke0yEBbxh0s4fOvimna7TEcBLPq0x06/K78AaABXrTVQM6S0vdocFl9EoNgU17hqA==", "dependencies": { "@docsearch/react": "^3.1.1", - "@docusaurus/core": "2.3.1", - "@docusaurus/logger": "2.3.1", - "@docusaurus/plugin-content-docs": "2.3.1", - "@docusaurus/theme-common": "2.3.1", - "@docusaurus/theme-translations": "2.3.1", - "@docusaurus/utils": "2.3.1", - "@docusaurus/utils-validation": "2.3.1", + "@docusaurus/core": "2.4.0", + "@docusaurus/logger": "2.4.0", + "@docusaurus/plugin-content-docs": "2.4.0", + "@docusaurus/theme-common": "2.4.0", + "@docusaurus/theme-translations": "2.4.0", + "@docusaurus/utils": "2.4.0", + "@docusaurus/utils-validation": "2.4.0", "algoliasearch": "^4.13.1", "algoliasearch-helper": "^3.10.0", "clsx": "^1.2.1", @@ -2502,9 +2503,9 @@ } }, "node_modules/@docusaurus/theme-translations": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-2.3.1.tgz", - "integrity": "sha512-BsBZzAewJabVhoGG1Ij2u4pMS3MPW6gZ6sS4pc+Y7czevRpzxoFNJXRtQDVGe7mOpv/MmRmqg4owDK+lcOTCVQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-2.4.0.tgz", + "integrity": "sha512-kEoITnPXzDPUMBHk3+fzEzbopxLD3fR5sDoayNH0vXkpUukA88/aDL1bqkhxWZHA3LOfJ3f0vJbOwmnXW5v85Q==", "dependencies": { "fs-extra": "^10.1.0", "tslib": "^2.4.0" @@ -2514,9 +2515,9 @@ } }, "node_modules/@docusaurus/types": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-2.3.1.tgz", - "integrity": "sha512-PREbIRhTaNNY042qmfSE372Jb7djZt+oVTZkoqHJ8eff8vOIc2zqqDqBVc5BhOfpZGPTrE078yy/torUEZy08A==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-2.4.0.tgz", + "integrity": "sha512-xaBXr+KIPDkIaef06c+i2HeTqVNixB7yFut5fBXPGI2f1rrmEV2vLMznNGsFwvZ5XmA3Quuefd4OGRkdo97Dhw==", "dependencies": { "@types/history": "^4.7.11", "@types/react": "*", @@ -2533,11 +2534,11 @@ } }, "node_modules/@docusaurus/utils": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.3.1.tgz", - "integrity": "sha512-9WcQROCV0MmrpOQDXDGhtGMd52DHpSFbKLfkyaYumzbTstrbA5pPOtiGtxK1nqUHkiIv8UwexS54p0Vod2I1lg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-2.4.0.tgz", + "integrity": "sha512-89hLYkvtRX92j+C+ERYTuSUK6nF9bGM32QThcHPg2EDDHVw6FzYQXmX6/p+pU5SDyyx5nBlE4qXR92RxCAOqfg==", "dependencies": { - "@docusaurus/logger": "2.3.1", + "@docusaurus/logger": "2.4.0", "@svgr/webpack": "^6.2.1", "escape-string-regexp": "^4.0.0", "file-loader": "^6.2.0", @@ -2567,9 +2568,9 @@ } }, "node_modules/@docusaurus/utils-common": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-2.3.1.tgz", - "integrity": "sha512-pVlRpXkdNcxmKNxAaB1ya2hfCEvVsLDp2joeM6K6uv55Oc5nVIqgyYSgSNKZyMdw66NnvMfsu0RBylcwZQKo9A==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-2.4.0.tgz", + "integrity": "sha512-zIMf10xuKxddYfLg5cS19x44zud/E9I7lj3+0bv8UIs0aahpErfNrGhijEfJpAfikhQ8tL3m35nH3hJ3sOG82A==", "dependencies": { "tslib": "^2.4.0" }, @@ -2586,12 +2587,12 @@ } }, "node_modules/@docusaurus/utils-validation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.3.1.tgz", - "integrity": "sha512-7n0208IG3k1HVTByMHlZoIDjjOFC8sbViHVXJx0r3Q+3Ezrx+VQ1RZ/zjNn6lT+QBCRCXlnlaoJ8ug4HIVgQ3w==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-2.4.0.tgz", + "integrity": "sha512-IrBsBbbAp6y7mZdJx4S4pIA7dUyWSA0GNosPk6ZJ0fX3uYIEQgcQSGIgTeSC+8xPEx3c16o03en1jSDpgQgz/w==", "dependencies": { - "@docusaurus/logger": "2.3.1", - "@docusaurus/utils": "2.3.1", + "@docusaurus/logger": "2.4.0", + "@docusaurus/utils": "2.4.0", "joi": "^17.6.0", "js-yaml": "^4.1.0", "tslib": "^2.4.0" @@ -3492,9 +3493,9 @@ "peer": true }, "node_modules/@types/mdast": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", - "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz", + "integrity": "sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==", "dependencies": { "@types/unist": "*" } @@ -4157,30 +4158,30 @@ } }, "node_modules/algoliasearch": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.14.3.tgz", - "integrity": "sha512-GZTEuxzfWbP/vr7ZJfGzIl8fOsoxN916Z6FY2Egc9q2TmZ6hvq5KfAxY89pPW01oW/2HDEKA8d30f9iAH9eXYg==", - "dependencies": { - "@algolia/cache-browser-local-storage": "4.14.3", - "@algolia/cache-common": "4.14.3", - "@algolia/cache-in-memory": "4.14.3", - "@algolia/client-account": "4.14.3", - "@algolia/client-analytics": "4.14.3", - "@algolia/client-common": "4.14.3", - "@algolia/client-personalization": "4.14.3", - "@algolia/client-search": "4.14.3", - "@algolia/logger-common": "4.14.3", - "@algolia/logger-console": "4.14.3", - "@algolia/requester-browser-xhr": "4.14.3", - "@algolia/requester-common": "4.14.3", - "@algolia/requester-node-http": "4.14.3", - "@algolia/transporter": "4.14.3" + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.17.0.tgz", + "integrity": "sha512-JMRh2Mw6sEnVMiz6+APsi7lx9a2jiDFF+WUtANaUVCv6uSU9UOLdo5h9K3pdP6frRRybaM2fX8b1u0nqICS9aA==", + "dependencies": { + "@algolia/cache-browser-local-storage": "4.17.0", + "@algolia/cache-common": "4.17.0", + "@algolia/cache-in-memory": "4.17.0", + "@algolia/client-account": "4.17.0", + "@algolia/client-analytics": "4.17.0", + "@algolia/client-common": "4.17.0", + "@algolia/client-personalization": "4.17.0", + "@algolia/client-search": "4.17.0", + "@algolia/logger-common": "4.17.0", + "@algolia/logger-console": "4.17.0", + "@algolia/requester-browser-xhr": "4.17.0", + "@algolia/requester-common": "4.17.0", + "@algolia/requester-node-http": "4.17.0", + "@algolia/transporter": "4.17.0" } }, "node_modules/algoliasearch-helper": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.11.3.tgz", - "integrity": "sha512-TbaEvLwiuGygHQIB8y+OsJKQQ40+JKUua5B91X66tMUHyyhbNHvqyr0lqd3wCoyKx7WybyQrC0WJvzoIeh24Aw==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.13.0.tgz", + "integrity": "sha512-kV3c1jMQCvkARtGsSDvAwuht4PAMSsQILqPiH4WFiARoa3jXJ/r1TQoBWAjWyWF48rsNYCv7kzxgB4LTxrvvuw==", "dependencies": { "@algolia/events": "^4.0.1" }, @@ -4382,9 +4383,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.13", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", - "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", + "version": "10.4.14", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", + "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", "funding": [ { "type": "opencollective", @@ -4396,8 +4397,8 @@ } ], "dependencies": { - "browserslist": "^4.21.4", - "caniuse-lite": "^1.0.30001426", + "browserslist": "^4.21.5", + "caniuse-lite": "^1.0.30001464", "fraction.js": "^4.2.0", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -4853,9 +4854,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001458", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001458.tgz", - "integrity": "sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w==", + "version": "1.0.30001486", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001486.tgz", + "integrity": "sha512-uv7/gXuHi10Whlj0pp5q/tsK/32J2QSqVRKQhs2j8VsDCjgyruAh/eEXHF822VqO9yT6iZKw3nRwZRSPBE9OQg==", "funding": [ { "type": "opencollective", @@ -4864,6 +4865,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -5333,9 +5338,9 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "node_modules/copy-text-to-clipboard": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.0.1.tgz", - "integrity": "sha512-rvVsHrpFcL4F2P8ihsoLdFHmd404+CMg71S756oRSeQgqk51U3kicGdnvfkrxva0xXH92SjGS62B0XIJsbh+9Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.1.0.tgz", + "integrity": "sha512-PFM6BnjLnOON/lB3ta/Jg7Ywsv+l9kQGD4TWDCSlRBGmqnnTM5MrDkhAFgw+8HZt0wW6Q2BBE4cmy9sq+s9Qng==", "engines": { "node": ">=12" }, @@ -6119,13 +6124,13 @@ } }, "node_modules/domutils": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", - "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", - "domhandler": "^5.0.1" + "domhandler": "^5.0.3" }, "funding": { "url": "https://github.com/fb55/domutils?sponsor=1" @@ -7531,9 +7536,9 @@ "devOptional": true }, "node_modules/flux": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.3.tgz", - "integrity": "sha512-yKAbrp7JhZhj6uiT1FTuVMlIAT1J4jqEyBpFApi1kxpGZCvacMVc/t1pMQyotqHhAgvoE3bNvAykhCo2CLjnYw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.4.tgz", + "integrity": "sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw==", "dependencies": { "fbemitter": "^3.0.0", "fbjs": "^3.0.1" @@ -8418,9 +8423,9 @@ } }, "node_modules/htmlparser2": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", - "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { @@ -8430,9 +8435,9 @@ ], "dependencies": { "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", + "domhandler": "^5.0.3", "domutils": "^3.0.1", - "entities": "^4.3.0" + "entities": "^4.4.0" } }, "node_modules/http-cache-semantics": { @@ -8644,9 +8649,9 @@ } }, "node_modules/infima": { - "version": "0.2.0-alpha.42", - "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.42.tgz", - "integrity": "sha512-ift8OXNbQQwtbIt6z16KnSWP7uJ/SysSMFI4F87MNRTicypfl4Pv3E2OGVv6N3nSZFJvA8imYulCBS64iyHYww==", + "version": "0.2.0-alpha.43", + "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.43.tgz", + "integrity": "sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ==", "engines": { "node": ">=12" } @@ -11212,9 +11217,9 @@ } }, "node_modules/postcss-sort-media-queries": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.3.0.tgz", - "integrity": "sha512-jAl8gJM2DvuIJiI9sL1CuiHtKM4s5aEIomkU8G3LFvbP+p8i7Sz8VV63uieTgoewGqKbi+hxBTiOKJlB35upCg==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.4.1.tgz", + "integrity": "sha512-QDESFzDDGKgpiIh4GYXsSy6sek2yAwQx1JASl5AxBtU1Lq2JfKBljIPNdil989NcSKRQX1ToiaKphImtBuhXWw==", "dependencies": { "sort-css-media-queries": "2.1.0" }, @@ -11832,11 +11837,11 @@ } }, "node_modules/react-textarea-autosize": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.4.0.tgz", - "integrity": "sha512-YrTFaEHLgJsi8sJVYHBzYn+mkP3prGkmP2DKb/tm0t7CLJY5t1Rxix8070LAKb0wby7bl/lf2EeHkuMihMZMwQ==", + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.4.1.tgz", + "integrity": "sha512-aD2C+qK6QypknC+lCMzteOdIjoMbNlgSFmJjCV+DrfTPwp59i/it9mMNf2HDzvRjQgKAyBDPyLJhcrzElf2U4Q==", "dependencies": { - "@babel/runtime": "^7.10.2", + "@babel/runtime": "^7.20.13", "use-composed-ref": "^1.3.0", "use-latest": "^1.2.1" }, @@ -13933,9 +13938,9 @@ } }, "node_modules/ua-parser-js": { - "version": "0.7.33", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz", - "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==", + "version": "0.7.35", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.35.tgz", + "integrity": "sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g==", "funding": [ { "type": "opencollective", diff --git a/package.json b/package.json index 82f24a25..0f42af56 100644 --- a/package.json +++ b/package.json @@ -10,15 +10,15 @@ "eslint": "eslint ./src", "eslint-fix": "npm run eslint -- --fix", "serve": "docusaurus serve", - "start": "docusaurus start", + "dev": "docusaurus start", "swizzle": "docusaurus swizzle", - "typecheck": "tsc", + "check-types": "tsc", "write-heading-ids": "docusaurus write-heading-ids", "write-translations": "docusaurus write-translations" }, "dependencies": { - "@docusaurus/core": "2.3.1", - "@docusaurus/preset-classic": "2.3.1", + "@docusaurus/core": "^2.4.0", + "@docusaurus/preset-classic": "^2.4.0", "@mdx-js/react": "^1.6.22", "@monaco-editor/react": "^4.5.0", "clsx": "^1.2.1", @@ -32,7 +32,7 @@ "remove": "^0.1.5" }, "devDependencies": { - "@docusaurus/module-type-aliases": "2.3.1", + "@docusaurus/module-type-aliases": "^2.4.0", "@tsconfig/docusaurus": "^1.0.5", "dts-bundle": "^0.7.3", "eslint": "^8.38.0", diff --git a/src/components/camera/cameraContext.tsx b/src/components/camera/cameraContext.tsx index 5717fbfb..3364d428 100644 --- a/src/components/camera/cameraContext.tsx +++ b/src/components/camera/cameraContext.tsx @@ -1,12 +1,4 @@ -import { Image, readCanvas } from 'image-js'; -import { - createContext, - Dispatch, - useContext, - useEffect, - useRef, - useState, -} from 'react'; +import { createContext, Dispatch, useContext, useEffect } from 'react'; export interface CameraState { cameras: MediaDeviceInfo[]; @@ -130,120 +122,6 @@ export function useVideoStream(videoRef: React.RefObject) { }, [selectedCamera, dispatch, videoRef]); } -export function useVideoTransform( - selectedDevice: MediaDeviceInfo, - processImage: (image: Image) => Image, - onFrame?: (inputImage: Image, outputImage: Image) => void, -) { - const videoRef = useRef(null); - const canvasInputRef = useRef(null); - const { dispatch } = useCameraContext(); - const [error, setError] = useState(''); - const onFrameRef = useRef(onFrame); - - useEffect(() => { - onFrameRef.current = onFrame; - }, [onFrame]); - - useEffect(() => { - const video = videoRef.current; - let nextFrameRequest: number; - let stream: MediaStream | null = null; - const abortController = new AbortController(); - abortController.signal.addEventListener('abort', () => { - if (stream) { - stream.getVideoTracks().forEach((track) => { - track.stop(); - }); - } - if (nextFrameRequest) { - cancelAnimationFrame(nextFrameRequest); - } - }); - if (!video) return; - if (selectedDevice === null) return; - // if selectedDevice is undefined, we make a first call to getUserMedia - // after which we can get the proper list of devices - - const constraints: MediaStreamConstraints = { - video: { - groupId: selectedDevice?.groupId - ? { - exact: selectedDevice.groupId, - } - : undefined, - deviceId: selectedDevice?.deviceId - ? { - exact: selectedDevice.deviceId, - } - : undefined, - - height: { ideal: 480, min: 480, max: 720 }, - width: { ideal: 640, min: 640, max: 1280 }, - }, - }; - - navigator.mediaDevices - .getUserMedia(constraints) - .then((mediaStream) => { - if (abortController.signal.aborted) { - mediaStream.getTracks().forEach((track) => { - track.stop(); - }); - return; - } - stream = mediaStream; - video.srcObject = stream; - video.onloadedmetadata = () => { - video - .play() - .then(() => { - const canvasInput = canvasInputRef.current as HTMLCanvasElement; - if (!canvasInput) return; - canvasInput.height = video.videoHeight; - canvasInput.width = video.videoWidth; - const inputContext = canvasInput.getContext('2d', { - willReadFrequently: true, - }) as CanvasRenderingContext2D; - function nextFrame() { - if (!video) return; - inputContext.drawImage(video, 0, 0); - const image = readCanvas(canvasInput); - try { - const outputImage = processImage(image); - onFrameRef.current?.(image, outputImage); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (err: any) { - setError(err.stack); - reportError(err); - } - nextFrameRequest = requestAnimationFrame(nextFrame); - } - nextFrameRequest = requestAnimationFrame(nextFrame); - }) - .catch(reportError); - }; - - return navigator.mediaDevices - .enumerateDevices() - .then((devices) => { - dispatch({ - type: 'SET_CAMERAS', - devices, - }); - }) - .catch(reportError); - }) - .catch(reportError); - - return () => { - abortController.abort(); - }; - }, [selectedDevice, dispatch, onFrameRef, processImage]); - - return { videoRef, canvasInputRef, error }; -} - export function filterAndSortDevices(devices: MediaDeviceInfo[]) { return devices .filter((device) => device.kind === 'videoinput') diff --git a/src/components/demo/ExpandableImageDuo.tsx b/src/components/demo/ExpandableImageDuo.tsx deleted file mode 100644 index 3e2224d1..00000000 --- a/src/components/demo/ExpandableImageDuo.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { decode, Image } from 'image-js'; -import React, { useEffect, useState } from 'react'; - -import { ExpandableImages, ImageSrc } from './ExpandableImages'; -import { FilterImageOption } from './importImageContext'; - -export default function ExpandableImageDuo({ - selectedImage, - processImage, -}: { - selectedImage: FilterImageOption; - processImage: (img: Image) => Image; -}) { - const [filteredImage, setFilteredImage] = useState( - null, - ); - - useEffect(() => { - if (selectedImage.type === 'url') { - fetch(selectedImage.value) - .then((response) => { - return response.arrayBuffer().then((buffer) => { - setFilteredImage(processImage(decode(new Uint8Array(buffer)))); - }); - }) - .catch((e: Error) => { - reportError(e); - setFilteredImage(e); - }); - } else { - try { - setFilteredImage(processImage(selectedImage.image)); - } catch (e: any) { - reportError(e); - setFilteredImage(e); - } - } - }, [selectedImage, processImage]); - - const expandableImages: ImageSrc[] = []; - if (filteredImage) { - expandableImages.push(filteredImage); - } - if (selectedImage.type === 'url') { - expandableImages.unshift(selectedImage.value); - } else { - expandableImages.unshift(selectedImage.image); - } - - if (filteredImage) { - return ; - } else { - return ( - <> - placeholder - placeholder - - ); - } -} diff --git a/src/components/demo/ImageDemo.tsx b/src/components/demo/ImageDemo.tsx deleted file mode 100644 index b50aa6a5..00000000 --- a/src/components/demo/ImageDemo.tsx +++ /dev/null @@ -1,281 +0,0 @@ -import { useDebounce } from '@site/src/hooks/useDebounce'; -import { ProcessImage } from '@site/src/types/IJS'; -import CodeBlock from '@theme/CodeBlock'; -import clsx from 'clsx'; -import { Image } from 'image-js'; -import * as IJS from 'image-js'; -import React, { - useState, - useMemo, - Dispatch, - SetStateAction, - ReactNode, - useEffect, -} from 'react'; -import { HiOutlineCodeBracket } from 'react-icons/hi2'; -import { RxCodesandboxLogo } from 'react-icons/rx'; - -import CameraImageButton from '../camera/CameraImageButton'; -import CameraStreamButton from '../camera/CameraStreamButton'; -import { - findCameraById, - getCameraId, - getCameraLabel, - useCameraContext, -} from '../camera/cameraContext'; -import CodeEditor from '../editor/CodeEditor'; - -import ExpandableImageDuo from './ExpandableImageDuo'; -import ExpandableVideoDuo from './ExpandableVideoDuo'; -import { ImageInputButton } from './ImageInputButton'; -import { convertCodeToFunction } from './convertCodeToFunction'; -import { - FilterImageOption, - ImageOption, - isImageOption, - isUrlOption, - useImportImageProvider, -} from './importImageContext'; - -function identity(img: Image) { - return img; -} - -type Addon = 'code' | 'editor'; -export default function ImageDemo({ - processImage, - code, - defaultEditorCode, -}: { - processImage: (img: Image) => Image; - code: string; - defaultEditorCode: string; -}) { - const [editorValue, setEditorValue] = useState(defaultEditorCode); - const [customFunction, setCustomFunction] = useState(identity); - const debouncedEditorValue = useDebounce(editorValue, 1000); - const { images, addImages, isVideoStreamAllowed } = useImportImageProvider(); - const [addon, setAddon] = useState(null); - const processAndCheck = useMemo(() => { - if (addon === 'editor') { - return processImageWithCheck(customFunction); - } else { - return processImageWithCheck(processImage); - } - }, [processImage, addon, customFunction]); - - useEffect(() => { - try { - const fn = convertCodeToFunction(debouncedEditorValue || ''); - setCustomFunction(() => fn); - } catch (err: any) { - setCustomFunction(() => () => { - throw err; - }); - } - }, [debouncedEditorValue]); - - const [selectedImage, setSelectedImage] = useState( - images[0], - ); - const [selectedDevice, setSelectedDevice] = useState( - null, - ); - - const { - cameraState: { cameras }, - } = useCameraContext(); - - const shownCameras = isVideoStreamAllowed ? cameras : []; - - const standardImages = images.filter(isUrlOption); - const customImages = images.filter((img) => isImageOption(img)); - - const selectedOption = selectedDevice - ? getCameraId(selectedDevice) - : selectedImage.value; - - return ( -
-
-
- {selectedDevice ? ( - - ) : ( - - )} -
- -
-
- - -
-
- - { - const newOptions: ImageOption[] = images.map((image) => ({ - type: 'image', - value: image.file.name, - image: image.image, - })); - addImages(newOptions); - if (newOptions.length) { - setSelectedImage(newOptions[newOptions.length - 1]); - setSelectedDevice(null); - } - }} - /> - { - const newOptions: ImageOption[] = [ - { - type: 'image', - value: name, - image, - }, - ]; - addImages(newOptions); - setSelectedImage(newOptions[0]); - setSelectedDevice(null); - }} - /> - - - - - - -
-
-
- {addon === 'code' && ( - {code} - )} - -
-
-
- ); -} - -function AddonButton(props: { - addon: Addon; - selectedAddon: Addon | null; - setSelectedAddon: Dispatch>; - children: ReactNode; -}) { - const { addon, selectedAddon, setSelectedAddon, children } = props; - return ( - - ); -} - -function processImageWithCheck(fn: ProcessImage) { - return (image: Image) => { - const img = fn(image, IJS); - if (!(img instanceof Image)) { - throw new Error('the function should return an image'); - } - return img; - }; -} diff --git a/src/components/editor/CodeEditor.tsx b/src/components/editor/CodeEditor.tsx index 44df6936..e96c02a2 100644 --- a/src/components/editor/CodeEditor.tsx +++ b/src/components/editor/CodeEditor.tsx @@ -50,20 +50,29 @@ function MonacoEditor({ monaco.editor.setTheme( colorMode === 'dark' ? 'vs-dark' : 'vs-light-modified', ); - editor.addCommand( - monaco.KeyMod.Alt | monaco.KeyCode.Space, - () => { - editor.trigger('', 'editor.action.triggerSuggest', ''); - }, - 'editorTextFocus && !editorHasSelection && ' + - '!editorHasMultipleSelections && !editorTabMovesFocus && ' + - '!hasQuickSuggest', - ); + function addCommand() { + editor.addCommand( + monaco.KeyMod.Alt | monaco.KeyCode.Space, + () => { + editor.trigger('', 'editor.action.triggerSuggest', ''); + }, + 'editorTextFocus && !editorHasSelection && ' + + '!editorHasMultipleSelections && !editorTabMovesFocus && ' + + '!hasQuickSuggest', + ); + } + + // This is a trick to prevent the command from being triggered on the wrong + // editor when multiple editors are present on the page. + // https://github.com/microsoft/monaco-editor/issues/2947 + editor.onDidFocusEditorText(() => { + addCommand(); + }); + editorRef.current = editor; } function handleBeforeMount(monaco: Monaco) { const javascript = monaco.languages.typescript.javascriptDefaults; - // @ts-expect-error raw-loader loads a string javascript.addExtraLib(imageJSTypes, 'image-js.d.ts'); javascript.setDiagnosticsOptions({ diff --git a/src/components/utils/useDebounce.tsx b/src/components/utils/useDebounce.tsx deleted file mode 100644 index c47621ca..00000000 --- a/src/components/utils/useDebounce.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { useEffect, useState } from 'react'; - -// Hook -export function useDebounce(value: T, delay: number) { - // State and setters for debounced value - const [debouncedValue, setDebouncedValue] = useState(() => value); - - useEffect( - () => { - // Update debounced value after delay - const handler = setTimeout(() => { - setDebouncedValue(() => value); - }, delay); - - // Cancel the timeout if value changes (also on delay change or unmount) - // This is how we prevent debounced value from updating if value is changed ... - // .. within the delay period. Timeout gets cleared and restarted. - return () => { - clearTimeout(handler); - }; - }, - [value, delay], // Only re-call effect if value or delay changes - ); - - return debouncedValue; -} diff --git a/src/css/custom.css b/src/css/custom.css index e068725b..07caa63d 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -68,10 +68,19 @@ button { height: 1.5em; } +.button-icon-lg svg { + width: 2em; + height: 2em; +} + .button-icon-selected { border-color: var(--ifm-button-border-color); } +.button-icon-disabled { + opacity: 0.5; +} + /* ===== Flex utilities ===== */ .flex-row { @@ -79,3 +88,9 @@ button { gap: 4; align-items: center; } + +.image-demo-toolbar-info { + color: var(--ifm-color-primary-lightest); + font-size: 0.75em; + line-height: 1em; +} diff --git a/src/demo/components/ImageDemo.tsx b/src/demo/components/ImageDemo.tsx new file mode 100644 index 00000000..feb4d579 --- /dev/null +++ b/src/demo/components/ImageDemo.tsx @@ -0,0 +1,68 @@ +import React from 'react'; + +import { useDemoStateContext } from '../contexts/demo/demoContext'; + +import ImageDemoAddon from './addons/ImageDemoAddon'; +import ExpandableImageDuo from './image/ExpandableImageDuo'; +import ImageDemoProvider from './providers/ImageDemoProvider'; +import ImageDemoToolbar from './toolbar/ImageDemoToolbar'; +import ExpandableVideoDuo from './video/ExpandableVideoDuo'; + +export default function ImageDemo({ + name, + code, + defaultEditorCode, + noAutoRun, +}: { + name: string; + code: string; + defaultEditorCode: string; + noAutoRun?: boolean; +}) { + return ( + +
+
+ + + +
+
+
+ ); +} + +function ImageDemoImages(props: { name: string }) { + const { selectedDevice, selectedImage, code } = useDemoStateContext(); + return ( +
+ {selectedDevice ? ( + + ) : ( + + )} +
+ ); +} diff --git a/src/demo/components/addons/AddonButton.tsx b/src/demo/components/addons/AddonButton.tsx new file mode 100644 index 00000000..a57ce14f --- /dev/null +++ b/src/demo/components/addons/AddonButton.tsx @@ -0,0 +1,30 @@ +import clsx from 'clsx'; +import React, { CSSProperties, ReactNode } from 'react'; + +import { + useDemoDispatchContext, + useDemoStateContext, +} from '../../contexts/demo/demoContext'; +import { Addon } from '../../utils/types'; + +export default function AddonButton(props: { + addon: Addon; + style?: CSSProperties; + children: ReactNode; +}) { + const { addon, children } = props; + const { addon: selectedAddon } = useDemoStateContext(); + const demoDispatch = useDemoDispatchContext(); + return ( + + ); +} diff --git a/src/demo/components/addons/CodeEditorAddon.tsx b/src/demo/components/addons/CodeEditorAddon.tsx new file mode 100644 index 00000000..fb6e1df6 --- /dev/null +++ b/src/demo/components/addons/CodeEditorAddon.tsx @@ -0,0 +1,69 @@ +import CodeEditor from '@site/src/components/editor/CodeEditor'; +import { useDebounce } from '@site/src/hooks/useDebounce'; +import clsx from 'clsx'; +import React, { useEffect, useState } from 'react'; +import { HiOutlinePlayCircle } from 'react-icons/hi2'; + +import { + useDemoDispatchContext, + useDemoStateContext, +} from '../../contexts/demo/demoContext'; +import { useImageRunState } from '../../contexts/run/imageRunContext'; +import { convertCodeToFunction } from '../../utils/convertCodeToFunction'; + +export default function CodeEditorAddon(props: { defaultEditorCode: string }) { + const runState = useImageRunState(); + const { addon, noAutoRun, code } = useDemoStateContext(); + const demoDispatch = useDemoDispatchContext(); + const [editorValue, setEditorValue] = useState(props.defaultEditorCode); + const debouncedEditorValue = useDebounce(editorValue, 1000); + + useEffect(() => { + if (!noAutoRun) { + // Check for syntax errors before dispatching the code + try { + convertCodeToFunction(debouncedEditorValue); + demoDispatch({ type: 'SET_CODE', payload: debouncedEditorValue }); + } catch (e) { + // Ignore + // The code editor should highlight the syntax error + } + } + }, [debouncedEditorValue, demoDispatch, noAutoRun]); + return ( +
+ + {addon === 'editor' && noAutoRun && ( +
+ + demoDispatch({ type: 'SET_CODE', payload: editorValue }) + } + /> +
+ )} +
+ ); +} + +function PlayButton(props: { disabled?: boolean; onClick: () => void }) { + const { disabled } = props; + return ( + + ); +} diff --git a/src/demo/components/addons/ErrorAddon.tsx b/src/demo/components/addons/ErrorAddon.tsx new file mode 100644 index 00000000..fcecb58f --- /dev/null +++ b/src/demo/components/addons/ErrorAddon.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +import { useImageRunState } from '../../contexts/run/imageRunContext'; + +export default function ErrorAddon() { + const runState = useImageRunState(); + + if (runState.error) { + return
{runState.error.message}
; + } else { + return
No error
; + } +} diff --git a/src/demo/components/addons/ImageDemoAddon.tsx b/src/demo/components/addons/ImageDemoAddon.tsx new file mode 100644 index 00000000..71894df4 --- /dev/null +++ b/src/demo/components/addons/ImageDemoAddon.tsx @@ -0,0 +1,25 @@ +import CodeBlock from '@theme/CodeBlock'; +import React from 'react'; + +import { useDemoStateContext } from '../../contexts/demo/demoContext'; + +import CodeEditorAddon from './CodeEditorAddon'; +import ErrorAddon from './ErrorAddon'; + +export default function ImageDemoAddon(props: { + code: string; + defaultEditorCode: string; +}) { + const { addon } = useDemoStateContext(); + + return ( +
+ {addon === 'code' && ( + {props.code} + )} + {addon === 'error' && } + {/* We always render this addon */} + +
+ ); +} diff --git a/src/components/demo/ImageInputButton.tsx b/src/demo/components/addons/ImageInputButton.tsx similarity index 100% rename from src/components/demo/ImageInputButton.tsx rename to src/demo/components/addons/ImageInputButton.tsx diff --git a/src/demo/components/image/ExpandableImageDuo.tsx b/src/demo/components/image/ExpandableImageDuo.tsx new file mode 100644 index 00000000..72a65edd --- /dev/null +++ b/src/demo/components/image/ExpandableImageDuo.tsx @@ -0,0 +1,111 @@ +import React, { useEffect } from 'react'; + +import { ImageDemoInputOption } from '../../contexts/importImage/importImageContext'; +import { + useImageRunDispatch, + useImageRunState, +} from '../../contexts/run/imageRunContext'; +import runAndDispatch from '../../contexts/run/runAndDispatch'; +import getJobManager from '../../worker/jobManager'; + +import { ExpandableImages, ImageSrc } from './ExpandableImages'; + +export default function ExpandableImageDuo({ + selectedImage, + code, + name, +}: { + selectedImage: ImageDemoInputOption; + code: string; + name: string; +}) { + const runState = useImageRunState(); + const runDispatch = useImageRunDispatch(); + + const { sourceImage, filteredImage } = runState.image || {}; + + useEffect(() => { + if (!selectedImage) { + return; + } + const jobManager = getJobManager(); + let imageOption = selectedImage; + if (imageOption.type === 'url') { + fetch(imageOption.value) + .then((response) => { + return response.arrayBuffer().then((buffer) => { + void runAndDispatch( + runDispatch, + { + type: 'encoded', + code, + data: new Uint8Array(buffer), + name, + }, + imageOption, + jobManager, + ); + }); + }) + .catch((err: any) => { + reportError(err); + }); + } else { + const rawImage = imageOption.image.getRawImage(); + void runAndDispatch( + runDispatch, + { + type: 'decoded', + code, + image: { + width: imageOption.image.width, + height: imageOption.image.height, + data: rawImage.data, + colorModel: imageOption.image.colorModel, + depth: rawImage.depth, + }, + name, + }, + imageOption, + jobManager, + ); + } + }, [selectedImage, code, name, runDispatch]); + + if (filteredImage && sourceImage) { + const expandableImages: ImageSrc[] = []; + if (sourceImage.type === 'url') { + expandableImages.push(sourceImage.value); + } else { + expandableImages.push(sourceImage.image); + } + expandableImages.push(filteredImage); + + return ( + + ); + } else { + return ( + <> + placeholder + placeholder + + ); + } +} diff --git a/src/components/demo/ExpandableImages.tsx b/src/demo/components/image/ExpandableImages.tsx similarity index 90% rename from src/components/demo/ExpandableImages.tsx rename to src/demo/components/image/ExpandableImages.tsx index f6e877db..aa8af805 100644 --- a/src/components/demo/ExpandableImages.tsx +++ b/src/demo/components/image/ExpandableImages.tsx @@ -13,9 +13,14 @@ import React, { } from 'react'; import { useKbs } from 'react-kbs'; +import { RunStatus } from '../../contexts/run/runReducer'; + export type ImageSrc = Image | string | Error; -export function ExpandableImages(props: { images: ImageSrc[] }) { +export function ExpandableImages(props: { + images: ImageSrc[]; + status: RunStatus; +}) { const { images } = props; const [current, setCurrent] = useState(0); const next = useCallback(() => { @@ -52,14 +57,12 @@ export function ExpandableImages(props: { images: ImageSrc[] }) { return ( -
+
{images.map((image, idx) => { if (image instanceof Error) { return ( // eslint-disable-next-line react/no-array-index-key -
- {image.message} -
+ ); } else { return ( @@ -96,6 +99,8 @@ export function ExpandableImages(props: { images: ImageSrc[] }) { > {typeof currentImage === 'string' ? ( + ) : currentImage instanceof Error ? ( + ) : ( )} @@ -209,3 +214,7 @@ function CanvasImage(props: { image: Image }) { }, [image, canvasRef]); return ; } + +function ImageError(props: { error: Error }) { + return
{props.error.message}
; +} diff --git a/src/demo/components/providers/ImageDemoProvider.tsx b/src/demo/components/providers/ImageDemoProvider.tsx new file mode 100644 index 00000000..739a39a9 --- /dev/null +++ b/src/demo/components/providers/ImageDemoProvider.tsx @@ -0,0 +1,35 @@ +import React from 'react'; + +import { + demoStateContext, + demoDispatchContext, +} from '../../contexts/demo/demoContext'; +import { + DemoInitialConfig, + useDemoReducer, +} from '../../contexts/demo/demoReducer'; +import { + imageRunStateContext, + imageRunDispatchContext, +} from '../../contexts/run/imageRunContext'; +import { useRunReducer } from '../../contexts/run/runReducer'; + +export default function ImageDemoProvider(props: { + children: React.ReactNode; + initial: DemoInitialConfig; +}) { + const [runState, runDispatch] = useRunReducer(); + const [demoState, demoDispatch] = useDemoReducer(props.initial); + + return ( + + + + + {props.children} + + + + + ); +} diff --git a/src/components/demo/ImportImage.tsx b/src/demo/components/providers/ImportImageProvider.tsx similarity index 68% rename from src/components/demo/ImportImage.tsx rename to src/demo/components/providers/ImportImageProvider.tsx index 6c09a9a5..4abcd340 100644 --- a/src/components/demo/ImportImage.tsx +++ b/src/demo/components/providers/ImportImageProvider.tsx @@ -1,23 +1,14 @@ import React, { ReactNode, useMemo, useReducer } from 'react'; +import { defaultImages } from '../../contexts/demo/defaultImages'; import { - FilterImageOption, - UrlOption, + ImageDemoInputOption, imageContext, -} from './importImageContext'; - -const defaultImages: UrlOption[] = [ - { type: 'url', value: '/img/standard/Lenna.png', label: 'Lenna' }, - { type: 'url', value: '/img/standard/barbara.jpg', label: 'Barbara' }, - { type: 'url', value: '/img/standard/boat.png', label: 'Standard boat' }, - { type: 'url', value: '/img/standard/mandrill.png', label: 'Mandrill' }, - { type: 'url', value: '/img/standard/peppers.png', label: 'Peppers' }, - { type: 'url', value: '/img/standard/house.png', label: 'House' }, -]; +} from '../../contexts/importImage/importImageContext'; export function ImportImageProvider(props: { children: ReactNode }) { const [images, addImages] = useReducer( - (state: FilterImageOption[], newOptions: FilterImageOption[]) => { + (state: ImageDemoInputOption[], newOptions: ImageDemoInputOption[]) => { newOptions.forEach((newOption) => { while (state.find((option) => option.value === newOption.value)) { if (state.find((option) => option.value === newOption.value)) { diff --git a/src/demo/components/snapshot/CameraImageButton.tsx b/src/demo/components/snapshot/CameraImageButton.tsx new file mode 100644 index 00000000..73831d99 --- /dev/null +++ b/src/demo/components/snapshot/CameraImageButton.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { HiOutlineCamera } from 'react-icons/hi2'; + +import { useOnOff } from '../../../hooks/useOnOff'; + +import CameraSnapshotModal, { Snapshot } from './CameraSnapshotModal'; + +export default function CameraImageButton({ + onSnapshot, +}: { + onSnapshot: (snapshot: Snapshot) => void; +}) { + const [isOpen, open, close] = useOnOff(false); + + return ( + <> + + {isOpen && } + + ); +} diff --git a/src/components/camera/CameraImageButton.tsx b/src/demo/components/snapshot/CameraSnapshotModal.tsx similarity index 67% rename from src/components/camera/CameraImageButton.tsx rename to src/demo/components/snapshot/CameraSnapshotModal.tsx index d8f50a15..36324b7f 100644 --- a/src/components/camera/CameraImageButton.tsx +++ b/src/demo/components/snapshot/CameraSnapshotModal.tsx @@ -1,53 +1,24 @@ +import { useImportImageContext } from '@site/src/demo/contexts/importImage/importImageContext'; import { Image } from 'image-js'; import React, { useRef, useState } from 'react'; -import { HiOutlineCamera } from 'react-icons/hi2'; import { useKbs } from 'react-kbs'; -import { useImportImageProvider } from '../demo/importImageContext'; -import Input from '../form/Input'; -import { useLockBodyScroll } from '../utils/useBodyScrollLock'; -import { useOnOff } from '../utils/useOnOff'; +import CameraFeed from '../../../components/camera/CameraFeed'; +import CameraSelector from '../../../components/camera/CameraSelector'; +import CameraSnapshotButton from '../../../components/camera/CameraSnapshotButton'; +import Input from '../../../components/form/Input'; +import { useLockBodyScroll } from '../../../hooks/useBodyScrollLock'; -import CameraFeed from './CameraFeed'; -import CameraSelector from './CameraSelector'; -import CameraSnapshotButton from './CameraSnapshotButton'; - -interface Snapshot { +export interface Snapshot { image: Image; name: string; } -export default function CameraImageButton({ - onSnapshot, -}: { - onSnapshot: (snapshot: Snapshot) => void; -}) { - const [isOpen, open, close] = useOnOff(false); - - return ( - <> - - {isOpen && } - - ); -} - -function CameraSnapshotModal(props: { +export default function CameraSnapshotModal(props: { close: () => void; onSnapshot: (snapshot: Snapshot) => void; }) { - const { images } = useImportImageProvider(); + const { images } = useImportImageContext(); const shortcutProps = useKbs([ { handler: () => props.close(), diff --git a/src/demo/components/toolbar/ImageDemoToolbar.tsx b/src/demo/components/toolbar/ImageDemoToolbar.tsx new file mode 100644 index 00000000..8e6ad3b8 --- /dev/null +++ b/src/demo/components/toolbar/ImageDemoToolbar.tsx @@ -0,0 +1,162 @@ +import React from 'react'; +import { + HiOutlineCodeBracket, + HiOutlineExclamationTriangle, +} from 'react-icons/hi2'; +import { RxCodesandboxLogo } from 'react-icons/rx'; + +import { + findCameraById, + getCameraId, + getCameraLabel, + useCameraContext, +} from '../../../components/camera/cameraContext'; +import { + useDemoDispatchContext, + useDemoStateContext, +} from '../../contexts/demo/demoContext'; +import { + ImageOption, + isImageOption, + isUrlOption, + useImportImageContext, +} from '../../contexts/importImage/importImageContext'; +import { useImageRunState } from '../../contexts/run/imageRunContext'; +import AddonButton from '../addons/AddonButton'; +import { ImageInputButton } from '../addons/ImageInputButton'; +import CameraImageButton from '../snapshot/CameraImageButton'; +import CameraStreamButton from '../video/CameraStreamButton'; + +import ImageDemoToolbarInfo from './ImageDemoToolbarInfo'; + +export default function ImageDemoToolbar() { + const { selectedImage, selectedDevice } = useDemoStateContext(); + const demoDispatch = useDemoDispatchContext(); + + const { images, addImages, isVideoStreamAllowed } = useImportImageContext(); + const runState = useImageRunState(); + + const { + cameraState: { cameras }, + } = useCameraContext(); + + const shownCameras = isVideoStreamAllowed ? cameras : []; + + const standardImages = images.filter(isUrlOption); + const customImages = images.filter((img) => isImageOption(img)); + + const selectedOption = selectedDevice + ? getCameraId(selectedDevice) + : selectedImage.value; + return ( +
+
+
+ + +
+ +
+
+ + { + const newOptions: ImageOption[] = images.map((image) => ({ + type: 'image', + value: image.file.name, + image: image.image, + })); + addImages(newOptions); + if (newOptions.length) { + demoDispatch({ + type: 'SET_SELECTED_IMAGE', + payload: newOptions[newOptions.length - 1], + }); + } + }} + /> + { + const newOptions: ImageOption[] = [ + { + type: 'image', + value: name, + image, + }, + ]; + addImages(newOptions); + demoDispatch({ + type: 'SET_SELECTED_IMAGE', + payload: newOptions[0], + }); + }} + /> + + + + + + + + + + +
+
+ ); +} diff --git a/src/demo/components/toolbar/ImageDemoToolbarInfo.tsx b/src/demo/components/toolbar/ImageDemoToolbarInfo.tsx new file mode 100644 index 00000000..f521d9c5 --- /dev/null +++ b/src/demo/components/toolbar/ImageDemoToolbarInfo.tsx @@ -0,0 +1,61 @@ +import React from 'react'; + +import useThrottle from '../../../hooks/useThrottle'; +import { useDemoStateContext } from '../../contexts/demo/demoContext'; +import { useImageRunState } from '../../contexts/run/imageRunContext'; +import useDebouncedStatus from '../../hooks/useDebouncedStatus'; + +export default function ImageDemoToolbarInfo() { + const { selectedDevice } = useDemoStateContext(); + + if (selectedDevice) { + return ; + } else { + return ; + } +} + +function FrameRateInfo() { + const runState = useImageRunState(); + + const meanTimeThrottled = useThrottle(runState.meanTime, 1000); + return ( +
+ {Math.round(1000 / meanTimeThrottled)} ops/s ( + {formatTime(meanTimeThrottled)}) +
+ ); +} + +function ImageInfo() { + const runState = useImageRunState(); + const debouncedStatus = useDebouncedStatus(runState.status); + return ( +
+ {debouncedStatus === 'success' && ( + + Ran in {formatTime(runState.time)} ({Math.round(1000 / runState.time)}{' '} + ops/s) + + )} + {debouncedStatus === 'error' && Processing failed} + {debouncedStatus === 'running' && Running...} +
+ ); +} + +function formatTime(time: number) { + // Time is in miliseconds + if (time >= 10 ** 4) { + return `${(time / 10 ** 3).toFixed(1)}s`; + } + if (time >= 10 ** 3) { + return `${(time / 10 ** 3).toFixed(2)}s`; + } else if (time >= 10) { + return `${Math.round(time)}ms`; + } else if (time > 0.1) { + return `${time.toFixed(2)}ms`; + } else { + return `${(time * 10 ** 3).toFixed(2)}μs`; + } +} diff --git a/src/components/camera/CameraStreamButton.tsx b/src/demo/components/video/CameraStreamButton.tsx similarity index 81% rename from src/components/camera/CameraStreamButton.tsx rename to src/demo/components/video/CameraStreamButton.tsx index d09eff43..5a305ef2 100644 --- a/src/components/camera/CameraStreamButton.tsx +++ b/src/demo/components/video/CameraStreamButton.tsx @@ -1,16 +1,18 @@ +import { useDebounce } from '@site/src/hooks/useDebounce'; import React, { useRef } from 'react'; import { HiOutlineVideoCamera } from 'react-icons/hi2'; -import { useImportImageProvider } from '../demo/importImageContext'; -import { useDebounce } from '../utils/useDebounce'; - -import { useCameraContext, useVideoStream } from './cameraContext'; +import { + useCameraContext, + useVideoStream, +} from '../../../components/camera/cameraContext'; +import { useImportImageContext } from '../../contexts/importImage/importImageContext'; export default function CameraStreamButton() { const { cameraState: { selectedCamera }, } = useCameraContext(); - const { isVideoStreamAllowed, allowVideoStream } = useImportImageProvider(); + const { isVideoStreamAllowed, allowVideoStream } = useImportImageContext(); const debouncedIsVideoStreamAllowed = useDebounce(isVideoStreamAllowed, 5000); diff --git a/src/components/demo/ExpandableVideoDuo.tsx b/src/demo/components/video/ExpandableVideoDuo.tsx similarity index 52% rename from src/components/demo/ExpandableVideoDuo.tsx rename to src/demo/components/video/ExpandableVideoDuo.tsx index 281cc76c..55cd0717 100644 --- a/src/components/demo/ExpandableVideoDuo.tsx +++ b/src/demo/components/video/ExpandableVideoDuo.tsx @@ -1,29 +1,40 @@ -import { Image } from 'image-js'; -import React, { useState } from 'react'; +import React, { useMemo } from 'react'; -import { useVideoTransform } from '../camera/cameraContext'; - -import { ExpandableImages } from './ExpandableImages'; +import { useImageRunState } from '../../contexts/run/imageRunContext'; +import { useVideoTransform } from '../../hooks/useVideoTransform'; +import { ExpandableImages } from '../image/ExpandableImages'; export default function ExpandableVideoDuo({ selectedDevice, - processImage, + code, + name, }: { selectedDevice: MediaDeviceInfo; - processImage: (img: Image) => Image; + code: string; + name: string; }) { - const [images, setImages] = useState([]); + const runState = useImageRunState(); const { videoRef, canvasInputRef } = useVideoTransform( selectedDevice, - processImage, - (inputImage, outputImage) => setImages([inputImage, outputImage]), + name, + code, ); + const images = useMemo(() => { + if (!runState.image) { + return null; + } + if (runState.image.sourceImage.type !== 'image') { + return null; + } + return [runState.image.sourceImage.image, runState.image.filteredImage]; + }, [runState.image]); + return ( <>