From 343bdac0b505dc06e96ee64ef210cea8f8296d85 Mon Sep 17 00:00:00 2001 From: Tiziano Vuksan Date: Fri, 19 Jan 2024 11:39:56 +0100 Subject: [PATCH 01/22] feat(webgui): added webgui module the webgui module represents alchemist simulation's data on a wep page. --- .../resources/graphql/EnvironmentSub.graphql | 11 + .../resources/graphql/NodeQuery.graphql | 5 + .../launchers/GraphQLServerLauncher.kt | 1 + alchemist-webgui/.kotlin-js-store/yarn.lock | 4025 +++++++++++++++++ alchemist-webgui/build.gradle.kts | 209 + alchemist-webgui/gradle.properties | 19 + .../src/commonMain/kotlin/ClientConnection.kt | 40 + alchemist-webgui/src/jsMain/kotlin/Client.kt | 55 + .../src/jsMain/kotlin/components/Content.kt | 179 + .../src/jsMain/kotlin/components/Navbar.kt | 10 + .../jsMain/kotlin/stores/EnvironmentState.kt | 14 + .../kotlin/stores/ScaleTranslateState.kt | 17 + .../stores/actions/EnvironmentStateAction.kt | 18 + .../stores/actions/ScaleTranslateAction.kt | 18 + .../stores/reducers/EnvironmentReducer.kt | 20 + .../stores/reducers/ScaleTranslateReducer.kt | 23 + .../src/jsMain/kotlin/ui/AppMain.kt | 54 + .../src/jsMain/resources/index.html | 21 + .../webpack.config.d/bootstrap.js | 3 + alchemist-webgui/webpack.config.d/css.js | 2 + alchemist-webgui/webpack.config.d/file.js | 6 + .../webpack.config.d/handlebars.js | 6 + alchemist-webgui/webpack.config.d/webpack.js | 17 + javadoc-io.json | 65 + settings.gradle.kts | 1 + 25 files changed, 4839 insertions(+) create mode 100644 alchemist-graphql/src/commonMain/resources/graphql/EnvironmentSub.graphql create mode 100644 alchemist-graphql/src/commonMain/resources/graphql/NodeQuery.graphql create mode 100644 alchemist-webgui/.kotlin-js-store/yarn.lock create mode 100644 alchemist-webgui/build.gradle.kts create mode 100644 alchemist-webgui/gradle.properties create mode 100644 alchemist-webgui/src/commonMain/kotlin/ClientConnection.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/Client.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/components/Content.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/components/Navbar.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentState.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/stores/ScaleTranslateState.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/stores/actions/EnvironmentStateAction.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/stores/actions/ScaleTranslateAction.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/stores/reducers/EnvironmentReducer.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/stores/reducers/ScaleTranslateReducer.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt create mode 100644 alchemist-webgui/src/jsMain/resources/index.html create mode 100644 alchemist-webgui/webpack.config.d/bootstrap.js create mode 100644 alchemist-webgui/webpack.config.d/css.js create mode 100644 alchemist-webgui/webpack.config.d/file.js create mode 100644 alchemist-webgui/webpack.config.d/handlebars.js create mode 100644 alchemist-webgui/webpack.config.d/webpack.js diff --git a/alchemist-graphql/src/commonMain/resources/graphql/EnvironmentSub.graphql b/alchemist-graphql/src/commonMain/resources/graphql/EnvironmentSub.graphql new file mode 100644 index 0000000000..db171f0962 --- /dev/null +++ b/alchemist-graphql/src/commonMain/resources/graphql/EnvironmentSub.graphql @@ -0,0 +1,11 @@ +subscription Environment { + environment{ + nodeToPos{ + entries{ + position{ + coordinates + } + } + } + } +} \ No newline at end of file diff --git a/alchemist-graphql/src/commonMain/resources/graphql/NodeQuery.graphql b/alchemist-graphql/src/commonMain/resources/graphql/NodeQuery.graphql new file mode 100644 index 0000000000..540056c223 --- /dev/null +++ b/alchemist-graphql/src/commonMain/resources/graphql/NodeQuery.graphql @@ -0,0 +1,5 @@ +query NodeQuery($id: Int! = 0) { + nodePosition(nodeId: $id) { + coordinates + } +} \ No newline at end of file diff --git a/alchemist-graphql/src/jvmMain/kotlin/it/unibo/alchemist/boundary/launchers/GraphQLServerLauncher.kt b/alchemist-graphql/src/jvmMain/kotlin/it/unibo/alchemist/boundary/launchers/GraphQLServerLauncher.kt index 44b1871913..c366e2747d 100644 --- a/alchemist-graphql/src/jvmMain/kotlin/it/unibo/alchemist/boundary/launchers/GraphQLServerLauncher.kt +++ b/alchemist-graphql/src/jvmMain/kotlin/it/unibo/alchemist/boundary/launchers/GraphQLServerLauncher.kt @@ -60,5 +60,6 @@ class GraphQLServerLauncher @JvmOverloads constructor( graphQLModule() graphQLRoutingModule() }, + ) } diff --git a/alchemist-webgui/.kotlin-js-store/yarn.lock b/alchemist-webgui/.kotlin-js-store/yarn.lock new file mode 100644 index 0000000000..a358b793c5 --- /dev/null +++ b/alchemist-webgui/.kotlin-js-store/yarn.lock @@ -0,0 +1,4025 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/runtime-corejs3@^7.23.6": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.23.8.tgz#b8aa3d47570bdd08fed77fdfd69542118af0df26" + integrity sha512-2ZzmcDugdm0/YQKFVYsXiwUN7USPX8PM7cytpb4PFl87fM+qYPSvTZX//8tyeJB1j0YDmafBJEbl5f8NfLyuKw== + dependencies: + core-js-pure "^3.30.2" + regenerator-runtime "^0.14.0" + +"@babel/runtime@^7.17.2", "@babel/runtime@^7.18.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.7": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9" + integrity sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg== + dependencies: + regenerator-runtime "^0.13.10" + +"@babel/runtime@^7.9.2": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.8.tgz#8ee6fe1ac47add7122902f257b8ddf55c898f650" + integrity sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw== + dependencies: + regenerator-runtime "^0.14.0" + +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + +"@discoveryjs/json-ext@^0.5.0": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@electron/get@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@electron/get/-/get-2.0.3.tgz#fba552683d387aebd9f3fcadbcafc8e12ee4f960" + integrity sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ== + dependencies: + debug "^4.1.1" + env-paths "^2.2.0" + fs-extra "^8.1.0" + got "^11.8.5" + progress "^2.0.3" + semver "^6.2.0" + sumchecker "^3.0.1" + optionalDependencies: + global-agent "^3.0.0" + +"@electron/remote@2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@electron/remote/-/remote-2.1.1.tgz#c1a95464957bfd38cfb79c6514f08b04a4db02dc" + integrity sha512-Lfxul2yBxL+FBVaKszNAkuUqSIDbUQ1I7BC394iRXyqA2XGz7im2bAxroNIM51jhySSPKUaOLHaFLxfV6pC9VQ== + +"@eonasdan/tempus-dominus@6.9.4": + version "6.9.4" + resolved "https://registry.yarnpkg.com/@eonasdan/tempus-dominus/-/tempus-dominus-6.9.4.tgz#cf3c9d223e38df2eaf18368927634bdd140c554f" + integrity sha512-zbGHdHCxjIuUN+31iTbsHhmrZuAalCdmuN3yf56P764Ko/tpOdWJUxr5yiBD5mvXoVcBxkGBtKCyDPL+bROABg== + +"@fortawesome/fontawesome-free@6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.1.tgz#55cc8410abf1003b726324661ce5b0d1c10de258" + integrity sha512-CNy5vSwN3fsUStPRLX7fUYojyuzoEMSXPl7zSLJ8TgtRfjv24LOnOWKT2zYwaHZCJGkdyRnTmstR0P+Ah503Gw== + +"@jridgewell/gen-mapping@^0.3.0": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" + integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.17": + version "0.3.19" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" + integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.17" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + +"@kurkle/color@^0.3.0": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@kurkle/color/-/color-0.3.2.tgz#5acd38242e8bde4f9986e7913c8fdf49d3aa199f" + integrity sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw== + +"@leichtgewicht/ip-codec@^2.0.1": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" + integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== + +"@orchidjs/sifter@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@orchidjs/sifter/-/sifter-1.0.3.tgz#43f42519472282eb632d0a1589184f044d64129b" + integrity sha512-zCZbwKegHytfsPm8Amcfh7v/4vHqTAaOu6xFswBYcn8nznBOuseu6COB2ON7ez0tFV0mKL0nRNnCiZZA+lU9/g== + dependencies: + "@orchidjs/unicode-variants" "^1.0.4" + +"@orchidjs/unicode-variants@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@orchidjs/unicode-variants/-/unicode-variants-1.0.4.tgz#6d2f812e3b19545bba2d81caffff1204de9a6a58" + integrity sha512-NvVBRnZNE+dugiXERFsET1JlKZfM5lJDEpSMilKW4bToYJ7pxf0Zne78xyXB2ny2c2aHfJ6WLnz1AaTNHAmQeQ== + +"@popperjs/core@2.11.8", "@popperjs/core@^2.11.5": + version "2.11.8" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" + integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== + +"@react-aria/ssr@^3.2.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@react-aria/ssr/-/ssr-3.4.0.tgz#a2b9a170214f56e41d3c4c933d0d8fcffa07a12a" + integrity sha512-qzuGk14/fUyUAoW/EBwgFcuMkVNXJVGlezTgZ1HovpCZ+p9844E7MUFHE7CuzFzPEIkVeqhBNIoIu+VJJ8YCOA== + dependencies: + "@babel/runtime" "^7.6.2" + +"@restart/hooks@^0.4.6", "@restart/hooks@^0.4.7": + version "0.4.7" + resolved "https://registry.yarnpkg.com/@restart/hooks/-/hooks-0.4.7.tgz#d79ca6472c01ce04389fc73d4a79af1b5e33cd39" + integrity sha512-ZbjlEHcG+FQtpDPHd7i4FzNNvJf2enAwZfJbpM8CW7BhmOAbsHpZe3tsHwfQUrBuyrxWqPYp2x5UMnilWcY22A== + dependencies: + dequal "^2.0.2" + +"@restart/ui@^1.3.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@restart/ui/-/ui-1.4.1.tgz#c9057915f0708c411824eeb16c03d0108c0208d2" + integrity sha512-J7wFOx2DcmkBqCqiZgDsggLO7faiNh4Nv1/v80FmbRgP+MYpwaVDKKXLC69DA4+ejgNIsBP5ORtC74EZqO1j8A== + dependencies: + "@babel/runtime" "^7.18.3" + "@popperjs/core" "^2.11.5" + "@react-aria/ssr" "^3.2.0" + "@restart/hooks" "^0.4.7" + "@types/warning" "^3.0.0" + dequal "^2.0.2" + dom-helpers "^5.2.0" + uncontrollable "^7.2.1" + warning "^4.0.3" + +"@rjaros/snabbdom-virtualize@1.0.0-beta.5": + version "1.0.0-beta.5" + resolved "https://registry.yarnpkg.com/@rjaros/snabbdom-virtualize/-/snabbdom-virtualize-1.0.0-beta.5.tgz#17f2586beee5e6c130e1b0ae2f589ff9162a86db" + integrity sha512-2rf1MI7wVSZLOgy+EXtOfYzqcwLtMjak4wzRBQUVJjO4qlCmlSo+bh890eKkILhRGxkp6aTQS9gMjH1fP9g8Nw== + dependencies: + html-parse-stringify "^3" + +"@sindresorhus/is@^4.0.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" + integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== + +"@socket.io/component-emitter@~3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" + integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== + +"@szmarczak/http-timer@^4.0.5": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" + integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== + dependencies: + defer-to-connect "^2.0.0" + +"@types/body-parser@*": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/bonjour@^3.5.9": + version "3.5.10" + resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.10.tgz#0f6aadfe00ea414edc86f5d106357cda9701e275" + integrity sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw== + dependencies: + "@types/node" "*" + +"@types/cacheable-request@^6.0.1": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" + integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "^3.1.4" + "@types/node" "*" + "@types/responselike" "^1.0.0" + +"@types/connect-history-api-fallback@^1.3.5": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae" + integrity sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw== + dependencies: + "@types/express-serve-static-core" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.35" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + +"@types/cookie@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" + integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== + +"@types/cors@^2.8.12": + version "2.8.12" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" + integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== + +"@types/eslint-scope@^3.7.3": + version "3.7.4" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" + integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.4.10" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.10.tgz#19731b9685c19ed1552da7052b6f668ed7eb64bb" + integrity sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" + integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== + +"@types/estree@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" + integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== + +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18": + version "4.17.31" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz#a1139efeab4e7323834bb0226e62ac019f474b2f" + integrity sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*", "@types/express@^4.17.13": + version "4.17.14" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.14.tgz#143ea0557249bc1b3b54f15db4c81c3d4eb3569c" + integrity sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.18" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/geojson@7946.0.10": + version "7946.0.10" + resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.10.tgz#6dfbf5ea17142f7f9a043809f1cd4c448cb68249" + integrity sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA== + +"@types/glob@5 - 7": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" + integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + +"@types/http-cache-semantics@*": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" + integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== + +"@types/http-proxy@^1.17.8": + version "1.17.9" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.9.tgz#7f0e7931343761efde1e2bf48c40f02f3f75705a" + integrity sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw== + dependencies: + "@types/node" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.11" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + +"@types/keyv@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" + integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== + dependencies: + "@types/node" "*" + +"@types/mime@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" + integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== + +"@types/minimatch@*": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" + integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== + +"@types/node@*", "@types/node@>=10.0.0": + version "18.11.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.9.tgz#02d013de7058cea16d36168ef2fc653464cfbad4" + integrity sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg== + +"@types/node@^18.11.18": + version "18.19.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.7.tgz#9a5f6ac7ec42a5dff68fe7faf2dd359710de4a12" + integrity sha512-IGRJfoNX10N/PfrReRZ1br/7SQ+2vF/tK3KXNwzXz82D32z5dMQEoOlFew18nLSN+vMNcLY4GrKfzwi/yWI8/w== + dependencies: + undici-types "~5.26.4" + +"@types/parse5@^5": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" + integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== + +"@types/prop-types@*": + version "15.7.5" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" + integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== + +"@types/qs@*": + version "6.9.7" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + +"@types/react-transition-group@^4.4.4": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416" + integrity sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA== + dependencies: + "@types/react" "*" + +"@types/react@*", "@types/react@>=16.9.11": + version "18.0.25" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.25.tgz#8b1dcd7e56fe7315535a4af25435e0bb55c8ae44" + integrity sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/responselike@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50" + integrity sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw== + dependencies: + "@types/node" "*" + +"@types/retry@0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" + integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== + +"@types/scheduler@*": + version "0.16.2" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" + integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== + +"@types/serve-index@^1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" + integrity sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg== + dependencies: + "@types/express" "*" + +"@types/serve-static@*", "@types/serve-static@^1.13.10": + version "1.15.0" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155" + integrity sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg== + dependencies: + "@types/mime" "*" + "@types/node" "*" + +"@types/sockjs@^0.3.33": + version "0.3.33" + resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f" + integrity sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw== + dependencies: + "@types/node" "*" + +"@types/warning@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52" + integrity sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA== + +"@types/ws@^8.5.1": + version "8.5.3" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" + integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w== + dependencies: + "@types/node" "*" + +"@types/yauzl@^2.9.1": + version "2.10.3" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999" + integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== + dependencies: + "@types/node" "*" + +"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" + integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" + integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== + +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" + integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== + +"@webassemblyjs/helper-buffer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" + integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== + +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" + integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" + integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== + +"@webassemblyjs/helper-wasm-section@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" + integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" + integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" + integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" + integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== + +"@webassemblyjs/wasm-edit@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" + integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-opt" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + "@webassemblyjs/wast-printer" "1.11.6" + +"@webassemblyjs/wasm-gen@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" + integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" + integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + +"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" + integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" + integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^2.1.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" + integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== + +"@webpack-cli/info@^2.0.1": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" + integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== + +"@webpack-cli/serve@^2.0.3": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" + integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +abab@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== + +abort-controller@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-import-assertions@^1.7.6: + version "1.8.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" + integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== + +acorn@^8.7.1: + version "8.8.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" + integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== + +acorn@^8.8.2: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv-keywords@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + +ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.0, ajv@^8.8.0: + version "8.11.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.2.tgz#aecb20b50607acf2569b6382167b65a96008bb78" + integrity sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-html-community@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" + integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +anymatch@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +array-flatten@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" + integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== + +async@^3.2.2: + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64id@2.0.0, base64id@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" + integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +body-parser@1.20.1, body-parser@^1.19.0: + version "1.20.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" + +bonjour-service@^1.0.11: + version "1.0.14" + resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.0.14.tgz#c346f5bc84e87802d08f8d5a60b93f758e514ee7" + integrity sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ== + dependencies: + array-flatten "^2.1.2" + dns-equal "^1.0.0" + fast-deep-equal "^3.1.3" + multicast-dns "^7.2.5" + +boolean@^3.0.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b" + integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw== + +bootstrap-fileinput@5.5.2: + version "5.5.2" + resolved "https://registry.yarnpkg.com/bootstrap-fileinput/-/bootstrap-fileinput-5.5.2.tgz#8acb1b5b3c3ec1c6b676c5c8503ba1c00e450911" + integrity sha512-3Axe1cNppy9DzaiiE/S4QdCxEcY4DwJ6CnO6n2bYAd7S7OAMY8WoF4yRdPwAq6dq5nIMj7Bd0ZVp/y07a6SChA== + dependencies: + bootstrap ">= 3.4.1" + jquery ">= 1.9.0" + opencollective-postinstall "^2.0.2" + +bootstrap-icons@1.11.3: + version "1.11.3" + resolved "https://registry.yarnpkg.com/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz#03f9cb754ec005c52f9ee616e2e84a82cab3084b" + integrity sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww== + +bootstrap@5.3.2, "bootstrap@>= 3.4.1": + version "5.3.2" + resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.3.2.tgz#97226583f27aae93b2b28ab23f4c114757ff16ae" + integrity sha512-D32nmNWiQHo94BKHLmOrdjlL05q1c8oxbtBphQFb9Z5to6eGRDCm0QgeaZ4zFBHzfg2++rqa2JkqCcxDy0sH0g== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +browserslist@^4.14.5: + version "4.21.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" + integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== + dependencies: + caniuse-lite "^1.0.30001400" + electron-to-chromium "^1.4.251" + node-releases "^2.0.6" + update-browserslist-db "^1.0.9" + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== + +cacheable-request@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817" + integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^6.0.1" + responselike "^2.0.0" + +call-bind@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +camelcase@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001400: + version "1.0.30001431" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz#e7c59bd1bc518fae03a4656be442ce6c4887a795" + integrity sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ== + +chalk@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chart.js@4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.4.1.tgz#ac5dc0e69a7758909158a96fe80ce43b3bb96a9f" + integrity sha512-C74QN1bxwV1v2PEujhmKjOZ7iUM4w6BWs23Md/6aOZZSlwMzeCIDGuZay++rBgChYru7/+QFeoQW0fQoP534Dg== + dependencies: + "@kurkle/color" "^0.3.0" + +chokidar@3.5.3, chokidar@^3.5.1, chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + +classnames@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" + integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +clone-response@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" + integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== + dependencies: + mimic-response "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^2.0.10, colorette@^2.0.14: + version "2.0.19" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" + integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== + +commander@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^6.0.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +connect-history-api-fallback@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== + +connect@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" + integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== + dependencies: + debug "2.6.9" + finalhandler "1.1.2" + parseurl "~1.3.3" + utils-merge "1.0.1" + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + +cookie@~0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + +core-js-pure@^3.30.2: + version "3.35.0" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.35.0.tgz#4660033304a050215ae82e476bd2513a419fbb34" + integrity sha512-f+eRYmkou59uh7BPcyJ8MC76DiGhspj1KMxVIcF24tzP8NA9HVa1uC7BTW2tgx7E1QVCzDzsgp7kArrzhlz8Ew== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cors@~2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +css-loader@6.8.1: + version "6.8.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.8.1.tgz#0f8f52699f60f5e679eab4ec0fcd68b8e8a50a88" + integrity sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g== + dependencies: + icss-utils "^5.1.0" + postcss "^8.4.21" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.3" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.2.0" + semver "^7.3.8" + +css-selector-parser@^1.3: + version "1.4.1" + resolved "https://registry.yarnpkg.com/css-selector-parser/-/css-selector-parser-1.4.1.tgz#03f9cb8a81c3e5ab2c51684557d5aaf6d2569759" + integrity sha512-HYPSb7y/Z7BNDCOrakL4raGO2zltZkbeXyAd6Tg9obzix6QhzxCotdBl6VT0Dv4vZfJGVz3WL/xaEI9Ly3ul0g== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +csstype@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" + integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== + +custom-event@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" + integrity sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg== + +date-format@^4.0.14: + version "4.0.14" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.14.tgz#7a8e584434fb169a521c8b7aa481f355810d9400" + integrity sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg== + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +default-gateway@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" + integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== + dependencies: + execa "^5.0.0" + +defer-to-connect@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + +define-data-property@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +define-properties@^1.1.3: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +dequal@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + +di@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" + integrity sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA== + +diff@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== + +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + integrity sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg== + +dns-packet@^5.2.2: + version "5.4.0" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.4.0.tgz#1f88477cf9f27e78a213fb6d118ae38e759a879b" + integrity sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g== + dependencies: + "@leichtgewicht/ip-codec" "^2.0.1" + +dom-helpers@^5.0.1, dom-helpers@^5.2.0, dom-helpers@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + +dom-serialize@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" + integrity sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ== + dependencies: + custom-event "~1.0.0" + ent "~2.2.0" + extend "^3.0.0" + void-elements "^2.0.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.4.251: + version "1.4.284" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" + integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== + +electron@28.1.1: + version "28.1.1" + resolved "https://registry.yarnpkg.com/electron/-/electron-28.1.1.tgz#37254967e32a4a69e18378f3b1aba1475522d08d" + integrity sha512-HJSbGHpRl46jWCp5G4OH57KSm2F5u15tB10ixD8iFiz9dhwojqlSQTRAcjSwvga+Vqs1jv7iqwQRrolXP4DgOA== + dependencies: + "@electron/get" "^2.0.0" + "@types/node" "^18.11.18" + extract-zip "^2.0.1" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +encoding@^0.1.12: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +engine.io-parser@~5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0" + integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg== + +engine.io@~6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.2.0.tgz#003bec48f6815926f2b1b17873e576acd54f41d0" + integrity sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg== + dependencies: + "@types/cookie" "^0.4.1" + "@types/cors" "^2.8.12" + "@types/node" ">=10.0.0" + accepts "~1.3.4" + base64id "2.0.0" + cookie "~0.4.1" + cors "~2.8.5" + debug "~4.3.1" + engine.io-parser "~5.0.3" + ws "~8.2.3" + +enhanced-resolve@^5.13.0: + version "5.15.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" + integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +ent@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" + integrity sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA== + +env-paths@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + +envinfo@^7.7.3: + version "7.8.1" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" + integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== + +es-module-lexer@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.3.0.tgz#6be9c9e0b4543a60cd166ff6f8b4e9dae0b0c16f" + integrity sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA== + +es6-error@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" + integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +express@^4.17.3: + version "4.18.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.1" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.5.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extract-zip@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== + dependencies: + debug "^4.1.1" + get-stream "^5.1.0" + yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +fastparse@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" + integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ== + +faye-websocket@^0.11.3: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== + dependencies: + pend "~1.2.0" + +fecha@4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" + integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +flatted@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" + integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== + +follow-redirects@^1.0.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + +format-util@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" + integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg== + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs-extra@^8.0.1, fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-monkey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" + integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +geojson@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/geojson/-/geojson-0.5.0.tgz#3cd6c96399be65b56ee55596116fe9191ce701c0" + integrity sha512-/Bx5lEn+qRF4TfQ5aLu6NH+UKtvIv7Lhc487y/c8BdludrCTpiWf9wyI0RTyqg49MFefIAvFDuEi5Dfd/zgNxQ== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.0.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" + integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.3" + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== + dependencies: + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +gettext-extract@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/gettext-extract/-/gettext-extract-2.0.1.tgz#4ade7e3d7c1c247d95a81f28059ca1923c355886" + integrity sha512-k51R90txkCZeXQEDjK0e7gNHfBHJVCu8IQ3AbSVZoOL5nR3yl+C+7qpzp6hoZwOtiniaY+CKeLyvNulsEUM6MA== + dependencies: + chalk "^2.3.0" + find-up "^4.1.0" + fs-extra "^8.0.1" + gettext-extractor "^3.1.0" + minimist "^1.2.0" + +gettext-extractor@^3.1.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/gettext-extractor/-/gettext-extractor-3.8.0.tgz#b2e8497a5952b039e5f488dca3d2e3aea2cb54b4" + integrity sha512-i/3mDQufQoJd2/EKm/B+VlaYrt3yGjVfLZu8DQpESKH29klNiW6z2S89FVCIEB85bDNgtGCeM/3A/yR1njr/Lw== + dependencies: + "@types/glob" "5 - 7" + "@types/parse5" "^5" + css-selector-parser "^1.3" + glob "5 - 7" + parse5 "5 - 6" + pofile "1.0.x" + typescript "4 - 5" + +gettext-parser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/gettext-parser/-/gettext-parser-2.0.0.tgz#c28bb31e61f76f14665dfafbb5c0518cdc1270ed" + integrity sha512-FDs/7XjNw58ToQwJFO7avZZbPecSYgw8PBYhd0An+4JtZSrSzKhEvTsVV2uqdO7VziWTOGSgLGD5YRPdsCjF7Q== + dependencies: + encoding "^0.1.12" + safe-buffer "^5.1.2" + +gettext-parser@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/gettext-parser/-/gettext-parser-1.4.0.tgz#f8baf34a292f03d5e42f02df099d301f167a7ace" + integrity sha512-sedZYLHlHeBop/gZ1jdg59hlUEcpcZJofLq2JFwJT1zTqAU3l2wFv6IsuwFHGqbiT9DWzMUW4/em2+hspnmMMA== + dependencies: + encoding "^0.1.12" + safe-buffer "^5.1.1" + +gettext-to-messageformat@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/gettext-to-messageformat/-/gettext-to-messageformat-0.3.1.tgz#c303ff9e31a5c781b14629b13bddb27b9ffca1f8" + integrity sha512-UyqIL3Ul4NryU95Wome/qtlcuVIqgEWVIFw0zi7Lv14ACLXfaVDCbrjZ7o+3BZ7u+4NS1mP/2O1eXZoHCoas8g== + dependencies: + gettext-parser "^1.4.0" + +gettext.js@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/gettext.js/-/gettext.js-2.0.2.tgz#2e3d7a0220b822dddcb61c6d905edb87da84cd9f" + integrity sha512-V5S0HCgOzHmTaaHUNMQr3xVPu6qLpv3j6K9Ygi41Pu6SRNNmWwvXCf/wQYXArznm4uZyZ1v8yrDZngxy6+kCyw== + dependencies: + po2json "^1.0.0-beta-3" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +"glob@5 - 7", glob@^7.1.3, glob@^7.1.7: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-agent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6" + integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q== + dependencies: + boolean "^3.0.1" + es6-error "^4.1.1" + matcher "^3.0.0" + roarr "^2.15.3" + semver "^7.3.2" + serialize-error "^7.0.1" + +globalthis@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +got@^11.8.5: + version "11.8.6" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" + integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== + dependencies: + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.2" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + +graceful-fs@^4.2.10: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + +handlebars-loader@1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/handlebars-loader/-/handlebars-loader-1.7.3.tgz#579b855770e51c325fbdf4075cca8d76fe10f59f" + integrity sha512-dDb+8D51vE3OTSE2wuGPWRAegtsEuw8Mk8hCjtRu/pNcBfN5q+M8ZG3kVJxBuOeBrVElpFStipGmaxSBTRR1mQ== + dependencies: + async "^3.2.2" + fastparse "^1.0.0" + loader-utils "1.4.x" + object-assign "^4.1.0" + +handlebars@4.7.8: + version "4.7.8" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" + integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.2" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== + dependencies: + get-intrinsic "^1.2.2" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-entities@^2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46" + integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA== + +html-parse-stringify@^3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2" + integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg== + dependencies: + void-elements "3.1.0" + +http-cache-semantics@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-parser-js@>=0.5.1: + version "0.5.8" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" + integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== + +http-proxy-middleware@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" + integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.2, iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== + +imask@7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/imask/-/imask-7.3.0.tgz#2b851ae8dc517f92cdd0d6dea0447bec9f27731d" + integrity sha512-TG+/rfb62JaQDM2KVrzEHMb4lv2srbsby7vHndXhqgQFB1MgPIXl60VQUfly/Xv5iWfA9ytB+rfQ+skUgINw7A== + dependencies: + "@babel/runtime-corejs3" "^7.23.6" + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imports-loader@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/imports-loader/-/imports-loader-4.0.1.tgz#a3d3067d0c369cd621e2b322b6b6bb2593756be5" + integrity sha512-ZiY+1xH5fqAJ1Qu1CFv+zw54rvPCC92DdYRDe0IIUboOhbJPLfOPbF/paA2iipelvMwjQUKWydcFOJ7f1+ZFcA== + dependencies: + source-map "^0.6.1" + strip-comments "^2.0.1" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== + +invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +ipaddr.js@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0" + integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isbinaryfile@^4.0.8: + version "4.0.10" + resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3" + integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jquery@3.7.1, "jquery@>= 1.9.0", jquery@^3.5.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.1.tgz#083ef98927c9a6a74d05a6af02806566d16274de" + integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg== + +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-stringify-safe@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + +json5@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== + optionalDependencies: + graceful-fs "^4.1.6" + +karma-chrome-launcher@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz#eb9c95024f2d6dfbb3748d3415ac9b381906b9a9" + integrity sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q== + dependencies: + which "^1.2.1" + +karma-junit-reporter@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/karma-junit-reporter/-/karma-junit-reporter-2.0.1.tgz#d34eef7f0b2fd064e0896954e8851a90cf14c8f3" + integrity sha512-VtcGfE0JE4OE1wn0LK8xxDKaTP7slN8DO3I+4xg6gAi1IoAHAXOJ1V9G/y45Xg6sxdxPOR3THCFtDlAfBo9Afw== + dependencies: + path-is-absolute "^1.0.0" + xmlbuilder "12.0.0" + +karma-mocha@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/karma-mocha/-/karma-mocha-2.0.1.tgz#4b0254a18dfee71bdbe6188d9a6861bf86b0cd7d" + integrity sha512-Tzd5HBjm8his2OA4bouAsATYEpZrp9vC7z5E5j4C5Of5Rrs1jY67RAwXNcVmd/Bnk1wgvQRou0zGVLey44G4tQ== + dependencies: + minimist "^1.2.3" + +karma-sourcemap-loader@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.4.0.tgz#b01d73f8f688f533bcc8f5d273d43458e13b5488" + integrity sha512-xCRL3/pmhAYF3I6qOrcn0uhbQevitc2DERMPH82FMnG+4WReoGcGFZb1pURf2a5apyrOHRdvD+O6K7NljqKHyA== + dependencies: + graceful-fs "^4.2.10" + +karma-webpack@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-5.0.0.tgz#2a2c7b80163fe7ffd1010f83f5507f95ef39f840" + integrity sha512-+54i/cd3/piZuP3dr54+NcFeKOPnys5QeM1IY+0SPASwrtHsliXUiCL50iW+K9WWA7RvamC4macvvQ86l3KtaA== + dependencies: + glob "^7.1.3" + minimatch "^3.0.4" + webpack-merge "^4.1.5" + +karma@6.4.2: + version "6.4.2" + resolved "https://registry.yarnpkg.com/karma/-/karma-6.4.2.tgz#a983f874cee6f35990c4b2dcc3d274653714de8e" + integrity sha512-C6SU/53LB31BEgRg+omznBEMY4SjHU3ricV6zBcAe1EeILKkeScr+fZXtaI5WyDbkVowJxxAI6h73NcFPmXolQ== + dependencies: + "@colors/colors" "1.5.0" + body-parser "^1.19.0" + braces "^3.0.2" + chokidar "^3.5.1" + connect "^3.7.0" + di "^0.0.1" + dom-serialize "^2.2.1" + glob "^7.1.7" + graceful-fs "^4.2.6" + http-proxy "^1.18.1" + isbinaryfile "^4.0.8" + lodash "^4.17.21" + log4js "^6.4.1" + mime "^2.5.2" + minimatch "^3.0.4" + mkdirp "^0.5.5" + qjobs "^1.2.0" + range-parser "^1.2.1" + rimraf "^3.0.2" + socket.io "^4.4.1" + source-map "^0.6.1" + tmp "^0.2.1" + ua-parser-js "^0.7.30" + yargs "^16.1.1" + +keyv@^4.0.0: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kvision-assets@8.0.9: + version "8.0.9" + resolved "https://registry.yarnpkg.com/kvision-assets/-/kvision-assets-8.0.9.tgz#3f4b4a5b637d0011b6f784b9d47fd95afe783392" + integrity sha512-JkuBQhmXa4x96QxeVXpor8u/go7mzHx7xaRvcg/3xbryJi9OenPze11Jn4SXPtk+7XGEMGXTodceNxLnOoK0ZQ== + +launch-editor@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.6.0.tgz#4c0c1a6ac126c572bd9ff9a30da1d2cae66defd7" + integrity sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ== + dependencies: + picocolors "^1.0.0" + shell-quote "^1.7.3" + +leaflet@1.9.4: + version "1.9.4" + resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.9.4.tgz#23fae724e282fa25745aff82ca4d394748db7d8d" + integrity sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA== + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +loader-utils@1.4.x: + version "1.4.2" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3" + integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash@^4.17.15, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +log4js@^6.4.1: + version "6.7.0" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.7.0.tgz#fff671a74b2f6e956d135c3c756c79072809a23b" + integrity sha512-KA0W9ffgNBLDj6fZCq/lRbgR6ABAodRIDHrZnS48vOtfKa4PzWImb0Md1lmGCdO3n3sbCm/n1/WmrNlZ8kCI3Q== + dependencies: + date-format "^4.0.14" + debug "^4.3.4" + flatted "^3.2.7" + rfdc "^1.3.0" + streamroller "^3.1.3" + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +matcher@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" + integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng== + dependencies: + escape-string-regexp "^4.0.0" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +memfs@^3.4.3: + version "3.4.11" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.11.tgz#3a34837ade675825d805a2c135e88cefe5e53aaf" + integrity sha512-GvsCITGAyDCxxsJ+X6prJexFQEhOCJaIlUbsAvjzSI5o5O7j2dle3jWvz5Z5aOdpOxW6ol3vI1+0ut+641F1+w== + dependencies: + fs-monkey "^1.0.3" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micromatch@^4.0.2: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^2.5.2: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.0.4, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0, minimist@^1.2.5: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +minimist@^1.2.3, minimist@^1.2.6: + version "1.2.7" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" + integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== + +mkdirp@^0.5.5: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mocha@10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" + integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== + dependencies: + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.4" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.2.0" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "5.0.1" + ms "2.1.3" + nanoid "3.3.3" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + workerpool "6.2.1" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multicast-dns@^7.2.5: + version "7.2.5" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" + integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== + dependencies: + dns-packet "^5.2.2" + thunky "^1.0.2" + +nanoid@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== + +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + +navigo@^8.11.1: + version "8.11.1" + resolved "https://registry.yarnpkg.com/navigo/-/navigo-8.11.1.tgz#aca756dbca40e2da2ae688a2a247ab4f070fcf4a" + integrity sha512-e3sc1UzakF+bWquC8/dbPCgo7LgPEW1ekgwb4pmEcl8tOc/I7lML8r7HBql+b0VRk7tJWTZqtkeObwJAVR1pxg== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +node-fetch@2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" + +node-forge@^1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + +node-releases@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" + integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.9.0: + version "1.12.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" + integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +onsenui@2.12.8: + version "2.12.8" + resolved "https://registry.yarnpkg.com/onsenui/-/onsenui-2.12.8.tgz#861490e84818881f9521fc000f4f92ac79758745" + integrity sha512-ju5c2nP/pW7ZIOALqG2yVmjlIa2S6Men2winRKB8E7qtMkjSpMcHD6aRLMZZ4bGRLIiubpT7psdXWLWOc/lEyA== + +open@^8.0.9: + version "8.4.0" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" + integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +opencollective-postinstall@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" + integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== + +p-cancelable@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" + integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-retry@^4.5.0: + version "4.6.2" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" + integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== + dependencies: + "@types/retry" "0.12.0" + retry "^0.13.1" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pace-progressbar@1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/pace-progressbar/-/pace-progressbar-1.0.10.tgz#0474af36b091b157e0ac74e708bfc48b6913e575" + integrity sha512-iaHL7mwzOcgctHJaPC5Hvs493l4gWc2x/oTnkz9vrB3XA1e8B/RZOGW9KFSZJIj3c6YQ7CXwKLGQcOl51pnEcQ== + +"parse5@5 - 6": + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +po2json@^1.0.0-beta-3: + version "1.0.0-beta-3" + resolved "https://registry.yarnpkg.com/po2json/-/po2json-1.0.0-beta-3.tgz#f087abeeccdc82c1e5315a749e5f3bcf7e1421e1" + integrity sha512-taS8y6ZEGzPAs0rygW9CuUPY8C3Zgx6cBy31QXxG2JlWS3fLxj/kuD3cbIfXBg30PuYN7J5oyBa/TIRjyqFFtg== + dependencies: + commander "^6.0.0" + gettext-parser "2.0.0" + gettext-to-messageformat "0.3.1" + +pofile@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pofile/-/pofile-1.0.11.tgz#35aff58c17491d127a07336d5522ebc9df57c954" + integrity sha512-Vy9eH1dRD9wHjYt/QqXcTz+RnX/zg53xK+KljFSX30PvdDMb2z+c6uDUeblUGqqJgz3QFsdlA0IJvHziPmWtQg== + +postcss-modules-extract-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" + integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== + +postcss-modules-local-by-default@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz#b08eb4f083050708998ba2c6061b50c2870ca524" + integrity sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA== + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.1.0.tgz#fbfddfda93a31f310f1d152c2bb4d3f3c5592ee0" + integrity sha512-SaIbK8XW+MZbd0xHPf7kdfA/3eOt7vxJ72IRecn3EzuZVLr1r0orzf0MX/pN8m+NMDoo6X/SQd8oeKqGZd8PXg== + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" + +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: + version "6.0.15" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz#11cc2b21eebc0b99ea374ffb9887174855a01535" + integrity sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@^8.4.21: + version "8.4.33" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.33.tgz#1378e859c9f69bf6f638b990a0212f43e2aaa742" + integrity sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +print-js@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/print-js/-/print-js-1.6.0.tgz#692b046cf31992b46afa6c6d8a9db1c69d431d1f" + integrity sha512-BfnOIzSKbqGRtO4o0rnj/K3681BSd2QUrsIZy/+WdCIugjIswjmx3lDEZpXB2ruGf9d4b3YNINri81+J0FsBWg== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +progress@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +prop-types-extra@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/prop-types-extra/-/prop-types-extra-1.1.1.tgz#58c3b74cbfbb95d304625975aa2f0848329a010b" + integrity sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew== + dependencies: + react-is "^16.3.2" + warning "^4.0.0" + +prop-types@^15.6.2, prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qjobs@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071" + integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg== + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +react-bootstrap@2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/react-bootstrap/-/react-bootstrap-2.5.0.tgz#e25e649e37f080d38eeb92ad5b4ed562a1d7de62" + integrity sha512-j/aLR+okzbYk61TM3eDOU1NqOqnUdwyVrF+ojoCRUxPdzc2R0xXvqyRsjSoyRoCo7n82Fs/LWjPCin/QJNdwvA== + dependencies: + "@babel/runtime" "^7.17.2" + "@restart/hooks" "^0.4.6" + "@restart/ui" "^1.3.1" + "@types/react-transition-group" "^4.4.4" + classnames "^2.3.1" + dom-helpers "^5.2.1" + invariant "^2.2.4" + prop-types "^15.8.1" + prop-types-extra "^1.1.0" + react-transition-group "^4.4.2" + uncontrollable "^7.2.1" + warning "^4.0.3" + +"react-dom@>=18.2.0 <19.0.0", react-dom@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.0" + +react-is@^16.13.1, react-is@^16.3.2: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + +react-transition-group@^4.4.2: + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + +"react@>=18.2.0 <19.0.0", react@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== + dependencies: + loose-envify "^1.1.0" + +readable-stream@^2.0.1: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== + dependencies: + resolve "^1.20.0" + +redux-thunk@2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.2.tgz#b9d05d11994b99f7a91ea223e8b04cf0afa5ef3b" + integrity sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q== + +"redux@>=4.1.2 <5.0.0": + version "4.2.1" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197" + integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w== + dependencies: + "@babel/runtime" "^7.9.2" + +regenerator-runtime@^0.13.10: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-alpn@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.20.0: + version "1.22.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" + integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +responselike@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" + integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== + dependencies: + lowercase-keys "^2.0.0" + +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + +rfdc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" + integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== + +rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +roarr@^2.15.3: + version "2.15.4" + resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd" + integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A== + dependencies: + boolean "^3.0.1" + detect-node "^2.0.4" + globalthis "^1.0.1" + json-stringify-safe "^5.0.1" + semver-compare "^1.0.0" + sprintf-js "^1.1.2" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== + dependencies: + loose-envify "^1.1.0" + +schema-utils@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" + integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^3.1.2: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7" + integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.8.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.0.0" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== + +selfsigned@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.1.1.tgz#18a7613d714c0cd3385c48af0075abf3f266af61" + integrity sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ== + dependencies: + node-forge "^1" + +semver-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow== + +semver@^6.2.0: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.2, semver@^7.3.8: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-error@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" + integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== + dependencies: + type-fest "^0.13.1" + +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + +serialize-javascript@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" + integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== + dependencies: + randombytes "^2.1.0" + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@^1.7.3: + version "1.8.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +snabbdom@3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/snabbdom/-/snabbdom-3.5.1.tgz#25f80ef15b194baea703d9d5441892e369de18e1" + integrity sha512-wHMNIOjkm/YNE5EM3RCbr/+DVgPg6AqQAX1eOxO46zYNvCXjKP5Y865tqQj3EXnaMBjkxmQA5jFuDpDK/dbfiA== + +socket.io-adapter@~2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz#b50a4a9ecdd00c34d4c8c808224daa1a786152a6" + integrity sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg== + +socket.io-parser@~4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.1.tgz#01c96efa11ded938dcb21cbe590c26af5eff65e5" + integrity sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + +socket.io@^4.4.1: + version "4.5.3" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.5.3.tgz#44dffea48d7f5aa41df4a66377c386b953bc521c" + integrity sha512-zdpnnKU+H6mOp7nYRXH4GNv1ux6HL6+lHL8g7Ds7Lj8CkdK1jJK/dlwsKDculbyOHifcJ0Pr/yeXnZQ5GeFrcg== + dependencies: + accepts "~1.3.4" + base64id "~2.0.0" + debug "~4.3.2" + engine.io "~6.2.0" + socket.io-adapter "~2.4.0" + socket.io-parser "~4.2.0" + +sockjs@^0.3.24: + version "0.3.24" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== + dependencies: + faye-websocket "^0.11.3" + uuid "^8.3.2" + websocket-driver "^0.7.4" + +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map-loader@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-4.0.1.tgz#72f00d05f5d1f90f80974eda781cbd7107c125f2" + integrity sha512-oqXpzDIByKONVY8g1NUPOTQhe0UTU5bWUl32GSkqK2LjJj0HmwTMVKxcUip0RgAYhY1mqgOxjbQM48a0mmeNfA== + dependencies: + abab "^2.0.6" + iconv-lite "^0.6.3" + source-map-js "^1.0.2" + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +split.js@1.6.5: + version "1.6.5" + resolved "https://registry.yarnpkg.com/split.js/-/split.js-1.6.5.tgz#f7f61da1044c9984cb42947df4de4fadb5a3f300" + integrity sha512-mPTnGCiS/RiuTNsVhCm9De9cCAUsrNFFviRbADdKiiV+Kk8HKp/0fWu7Kr8pi3/yBmsqLFHuXGT9UUZ+CNLwFw== + +sprintf-js@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +"statuses@>= 1.4.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +streamroller@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.1.3.tgz#d95689a8c29b30d093525d0baffe6616fd62ca7e" + integrity sha512-CphIJyFx2SALGHeINanjFRKQ4l7x2c+rXYJ4BMq0gd+ZK0gi4VT8b+eHe2wi58x4UayBAKx4xtHpXT/ea1cz8w== + dependencies: + date-format "^4.0.14" + debug "^4.3.4" + fs-extra "^8.1.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-comments@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-comments/-/strip-comments-2.0.1.tgz#4ad11c3fbcac177a67a40ac224ca339ca1c1ba9b" + integrity sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-json-comments@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +style-loader@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.3.tgz#bba8daac19930169c0c9c96706749a597ae3acff" + integrity sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw== + +sumchecker@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42" + integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg== + dependencies: + debug "^4.1.0" + +supports-color@8.1.1, supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tabulator-tables@5.5.2: + version "5.5.2" + resolved "https://registry.yarnpkg.com/tabulator-tables/-/tabulator-tables-5.5.2.tgz#2d0830363127e47bddbdda7725db5771a7f74095" + integrity sha512-GlzUPXeH9Rw1iPXcl3uBLJk6Sz51xdwscZ5RrsKxDQDQ+t2g31hGq/0xjaj6ea62wHgl8vzVsHuwap0jKzsUNg== + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +terser-webpack-plugin@^5.3.7: + version "5.3.9" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1" + integrity sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.17" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.16.8" + +terser@^5.16.8: + version "5.19.2" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.19.2.tgz#bdb8017a9a4a8de4663a7983f45c506534f9234e" + integrity sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + +tmp@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toastify-js@1.12.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/toastify-js/-/toastify-js-1.12.0.tgz#cc1c4f5c7e7380e854e20bedceb51980ea29f64d" + integrity sha512-HeMHCO9yLPvP9k0apGSdPUWrUbLnxUKNFzgUoZp1PHCLploIX/4DSQ7V8H25ef+h4iO9n0he7ImfcndnN6nDrQ== + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +tom-select@2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tom-select/-/tom-select-2.3.1.tgz#df338d9082874cd0bceb3bee87ed0184447c47f1" + integrity sha512-QS4vnOcB6StNGqX4sGboGXL2fkhBF2gIBB+8Hwv30FZXYPn0CyYO8kkdATRvwfCTThxiR4WcXwKJZ3cOmtI9eg== + dependencies: + "@orchidjs/sifter" "^1.0.3" + "@orchidjs/unicode-variants" "^1.0.4" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +trix@2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/trix/-/trix-2.0.8.tgz#6fff81b9e02c4f60a181febb3f6e862fbb98f1c4" + integrity sha512-tPqJl+NjALVUOC8sNfRrY42K7cfp4QBb9svXxmfhxYrspF9fOOzXq+GnDa9l2ZAcfM43msnPpkJ7MrJQA3FgrA== + +type-fest@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" + integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +"typescript@4 - 5": + version "5.3.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" + integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== + +typescript@5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" + integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + +ua-parser-js@^0.7.30: + version "0.7.32" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.32.tgz#cd8c639cdca949e30fa68c44b7813ef13e36d211" + integrity sha512-f9BESNVhzlhEFf2CHMSj40NWOjYPl1YKYbrvIr/hFTDEmLq7SRbWvm7FcdcpCYT95zrOhC7gZSxjdnnTpBcwVw== + +uglify-js@^3.1.4: + version "3.17.4" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" + integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== + +uncontrollable@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/uncontrollable/-/uncontrollable-7.2.1.tgz#1fa70ba0c57a14d5f78905d533cf63916dc75738" + integrity sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ== + dependencies: + "@babel/runtime" "^7.6.3" + "@types/react" ">=16.9.11" + invariant "^2.2.4" + react-lifecycles-compat "^3.0.4" + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.0.9: + version "1.0.10" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" + integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +void-elements@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" + integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== + +void-elements@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung== + +warning@^4.0.0, warning@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + +watchpack@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +webpack-cli@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.0.tgz#abc4b1f44b50250f2632d8b8b536cfe2f6257891" + integrity sha512-a7KRJnCxejFoDpYTOwzm5o21ZXMaNqtRlvS183XzGDUPRdVEzJNImcQokqYZ8BNTnk9DkKiuWxw75+DCCoZ26w== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^2.1.0" + "@webpack-cli/info" "^2.0.1" + "@webpack-cli/serve" "^2.0.3" + colorette "^2.0.14" + commander "^10.0.1" + cross-spawn "^7.0.3" + envinfo "^7.7.3" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^3.1.1" + rechoir "^0.8.0" + webpack-merge "^5.7.3" + +webpack-dev-middleware@^5.3.1: + version "5.3.3" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f" + integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA== + dependencies: + colorette "^2.0.10" + memfs "^3.4.3" + mime-types "^2.1.31" + range-parser "^1.2.1" + schema-utils "^4.0.0" + +webpack-dev-server@4.15.0: + version "4.15.0" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.15.0.tgz#87ba9006eca53c551607ea0d663f4ae88be7af21" + integrity sha512-HmNB5QeSl1KpulTBQ8UT4FPrByYyaLxpJoQ0+s7EvUrMc16m0ZS1sgb1XGqzmgCPk0c9y+aaXxn11tbLzuM7NQ== + dependencies: + "@types/bonjour" "^3.5.9" + "@types/connect-history-api-fallback" "^1.3.5" + "@types/express" "^4.17.13" + "@types/serve-index" "^1.9.1" + "@types/serve-static" "^1.13.10" + "@types/sockjs" "^0.3.33" + "@types/ws" "^8.5.1" + ansi-html-community "^0.0.8" + bonjour-service "^1.0.11" + chokidar "^3.5.3" + colorette "^2.0.10" + compression "^1.7.4" + connect-history-api-fallback "^2.0.0" + default-gateway "^6.0.3" + express "^4.17.3" + graceful-fs "^4.2.6" + html-entities "^2.3.2" + http-proxy-middleware "^2.0.3" + ipaddr.js "^2.0.1" + launch-editor "^2.6.0" + open "^8.0.9" + p-retry "^4.5.0" + rimraf "^3.0.2" + schema-utils "^4.0.0" + selfsigned "^2.1.1" + serve-index "^1.9.1" + sockjs "^0.3.24" + spdy "^4.0.2" + webpack-dev-middleware "^5.3.1" + ws "^8.13.0" + +webpack-merge@^4.1.5: + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== + dependencies: + lodash "^4.17.15" + +webpack-merge@^5.7.3: + version "5.8.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" + integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== + dependencies: + clone-deep "^4.0.1" + wildcard "^2.0.0" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@5.82.0: + version "5.82.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.82.0.tgz#3c0d074dec79401db026b4ba0fb23d6333f88e7d" + integrity sha512-iGNA2fHhnDcV1bONdUu554eZx+XeldsaeQ8T67H6KKHl2nUSwX8Zm7cmzOA46ox/X1ARxf7Bjv8wQ/HsB5fxBg== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.0" + "@webassemblyjs/ast" "^1.11.5" + "@webassemblyjs/wasm-edit" "^1.11.5" + "@webassemblyjs/wasm-parser" "^1.11.5" + acorn "^8.7.1" + acorn-import-assertions "^1.7.6" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.13.0" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.1.2" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.7" + watchpack "^2.4.0" + webpack-sources "^3.2.3" + +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" + integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== + +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" + integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== + +ws@^8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + +ws@~8.2.3: + version "8.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" + integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== + +xmlbuilder@12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-12.0.0.tgz#e2ed675e06834a089ddfb84db96e2c2b03f78c1a" + integrity sha512-lMo8DJ8u6JRWp0/Y4XLa/atVDr75H9litKlb2E5j3V3MesoL50EBgZDWoLT3F/LztVnG67GjPXLZpqcky/UMnQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@16.2.0, yargs@^16.1.1: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/alchemist-webgui/build.gradle.kts b/alchemist-webgui/build.gradle.kts new file mode 100644 index 0000000000..6a884b4fbe --- /dev/null +++ b/alchemist-webgui/build.gradle.kts @@ -0,0 +1,209 @@ +import Libs.alchemist +import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +group = "it.unibo.alchemist" +val alchemistGroup = "Run Alchemist" +val kvisionVersion = "7.3.1" + +plugins { + application + alias(libs.plugins.kotest.multiplatform) + alias(libs.plugins.kotlin.serialization) + alias(libs.plugins.ktor) + alias(libs.plugins.graphql.server) + alias(libs.plugins.graphql.client) + // id("io.kvision") version "7.3.1" +} + +repositories { + mavenCentral() + mavenLocal() +} + +application { + mainClass.set("it.unibo.alchemist.Alchemist") +} + +val task by tasks.register("runWEB") { + group = alchemistGroup + description = "Launches simulation" + + // Custom property to store the simulation file path + val simulationFile = project.objects.property(String::class.java) + + // Set the property value from the command line or use a default value + simulationFile.set(project.findProperty("simulationFile") as String? ?: "C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/Alchemist fork/Alchemist/test.yml") + // effect.set(project.findProperty("effect") as String? ?: "") + + // Add the simulation file path as a program argument + args("run", "C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/yaml/" + simulationFile.get()) + + classpath(tasks.named("compileKotlinJvm"), configurations.named("jvmRuntimeClasspath")) + + // , "-g","C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/effects/" + effect.get() +} + +tasks { + named("run", JavaExec::class).configure { + classpath(named("compileKotlinJvm"), configurations.named("jvmRuntimeClasspath")) + // val simulationFile = project.objects.property(String::class.java) + // args("run", "C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/yaml/" + simulationFile.get()) + } +} + +tasks.withType().configureEach { + if (name == "sourcesJar") { + dependsOn("kspCommonMainKotlinMetadata") + } +} + +tasks.named("sourcesJar").configure { + dependsOn("kspCommonMainKotlinMetadata") +} + +kotlin { + jvm { + withJava() + } + js(IR) { + browser { + runTask( + Action { + mainOutputFileName = "alchemist-webgui.js" + sourceMaps = false + devServer = KotlinWebpackConfig.DevServer( + open = false, + port = 3000, + proxy = mutableMapOf( + "/kv/*" to "http://localhost:8080", + "/kvws/*" to mapOf("target" to "ws://localhost:8080", "ws" to true), + ), + static = mutableListOf("${layout.buildDirectory.asFile.get()}/processedResources/js/main"), + ) + }, + ) + + webpackTask( + Action { + mainOutputFileName = "alchemist-webgui.js" + }, + ) + + /*testTask(Action { + useKarma { + useChromeHeadless() + } + })*/ + + binaries.executable() + } + } + sourceSets { + val commonMain by getting { + dependencies { + implementation(alchemist("web-renderer")) + implementation(alchemist("graphql")) + implementation(libs.apollo.runtime) + implementation(libs.korim) + implementation(libs.kotlin.stdlib) + implementation(libs.kotlinx.serialization.json) + implementation(libs.ktor.client.core) + implementation(libs.redux.kotlin.threadsafe) + } + } + + /*val commonTest by getting { + dependencies { + implementation(alchemist("graphql")) + //implementation(alchemist("api")) + implementation(alchemist("full")) + implementation(libs.apollo.runtime) + implementation(libs.kotlin.coroutines.core) + implementation(libs.kotlin.stdlib) + implementation(libs.kotlinx.serialization.json) + + implementation(libs.ktor.server.websockets) + implementation(libs.bundles.graphql.server) + implementation(libs.bundles.ktor.server) + + implementation(libs.kotest.assertions) + implementation(libs.kotest.runner) + implementation(libs.ktor.server.test.host) + } + }*/ + + val jvmMain by getting { + dependencies { + implementation(alchemist("graphql")) + implementation(libs.bundles.ktor.server) + implementation(libs.logback) + implementation(libs.resourceloader) + implementation(libs.apollo.runtime) + implementation(libs.korim) + implementation(libs.kotlin.stdlib) + implementation(libs.kotlinx.serialization.json) + implementation(libs.ktor.client.core) + implementation(libs.redux.kotlin.threadsafe) + } + } + + val jsMain by getting { + dependencies { + implementation(kotlin("stdlib-js")) + implementation(libs.apollo.runtime) + implementation(libs.bundles.ktor.client) + implementation(libs.bundles.kotlin.react) + + /*implementation("io.kvision:kvision:$kvisionVersion") + implementation("io.kvision:kvision-bootstrap:$kvisionVersion") + implementation("io.kvision:kvision-datetime:$kvisionVersion") + implementation("io.kvision:kvision-richtext:$kvisionVersion") + implementation("io.kvision:kvision-tom-select:$kvisionVersion") + implementation("io.kvision:kvision-bootstrap-upload:$kvisionVersion") + implementation("io.kvision:kvision-imask:$kvisionVersion") + implementation("io.kvision:kvision-toastify:$kvisionVersion") + implementation("io.kvision:kvision-fontawesome:$kvisionVersion") + implementation("io.kvision:kvision-bootstrap-icons:$kvisionVersion") + implementation("io.kvision:kvision-pace:$kvisionVersion") + implementation("io.kvision:kvision-handlebars:$kvisionVersion") + implementation("io.kvision:kvision-chart:$kvisionVersion") + implementation("io.kvision:kvision-tabulator:$kvisionVersion") + implementation("io.kvision:kvision-maps:$kvisionVersion") + implementation("io.kvision:kvision-rest:$kvisionVersion") + implementation("io.kvision:kvision-jquery:$kvisionVersion") + implementation("io.kvision:kvision-routing-navigo-ng:$kvisionVersion") + implementation("io.kvision:kvision-state:$kvisionVersion") + implementation("io.kvision:kvision-state-flow:$kvisionVersion") + implementation("io.kvision:kvision-ballast:$kvisionVersion") + implementation("io.kvision:kvision-redux-kotlin:$kvisionVersion")*/ + } + } + + /*val jsTest by getting { + dependencies{ + implementation("io.kvision:kvision-testutils:$kvisionVersion") + } + + }*/ + /*val jsTest by getting { + dependencies { + implementation(alchemist("graphql")) + //implementation(alchemist("api")) + implementation(alchemist("full")) + implementation(libs.apollo.runtime) + implementation(libs.kotlin.coroutines.core) + implementation(libs.kotlin.stdlib) + implementation(libs.kotlinx.serialization.json) + + implementation(libs.ktor.server.websockets) + implementation(libs.bundles.graphql.server) + implementation(libs.bundles.ktor.server) + + implementation(libs.kotest.assertions) + implementation(libs.kotest.runner) + implementation(libs.ktor.server.test.host) + } + }*/ + } +} diff --git a/alchemist-webgui/gradle.properties b/alchemist-webgui/gradle.properties new file mode 100644 index 0000000000..ad96a7b2e9 --- /dev/null +++ b/alchemist-webgui/gradle.properties @@ -0,0 +1,19 @@ +# +# Copyright (C) 2010-2023, Danilo Pianini and contributors +# listed, for each module, in the respective subproject's build.gradle.kts file. +# +# This file is part of Alchemist, and is distributed under the terms of the +# GNU General Public License, with a linking exception, +# as described in the file LICENSE in the Alchemist distribution's top directory. +# + +group = it.unibo.alchemist +artifactId = alchemist-webgui +projectLongName = Alchemist Web Gui +projectDescription = Alchemist module that starts a web server and allows to visualize an environment on a web browser. + +kotlin.code.style=official +kotlin.mpp.stability.nowarn=true + +#Dependencies +systemProp.kvisionVersion=7.3.1 \ No newline at end of file diff --git a/alchemist-webgui/src/commonMain/kotlin/ClientConnection.kt b/alchemist-webgui/src/commonMain/kotlin/ClientConnection.kt new file mode 100644 index 0000000000..b531627cd1 --- /dev/null +++ b/alchemist-webgui/src/commonMain/kotlin/ClientConnection.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +/*import com.apollographql.apollo3.api.Optional +import it.unibo.alchemist.boundary.graphql.client.GraphQLClient +import it.unibo.alchemist.boundary.graphql.client.GraphQLClientFactory +import it.unibo.alchemist.boundary.graphql.client.EnvironmentSubscription +import it.unibo.alchemist.boundary.graphql.client.GraphQLClient +import it.unibo.alchemist.boundary.graphql.client.GraphQLClientFactory +import it.unibo.alchemist.boundary.graphql.client.NodeQuery +import it.unibo.alchemist.boundary.webui.common.model.surrogate.Position2DSurrogate +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.first*/ + +open class ClientConnection() { + + // private val client: GraphQLClient = GraphQLClientFactory.subscriptionClient() + + // private val pos: Position2DSurrogate = Position2DSurrogate(5.0,6.0) + + /*suspend fun retrieveQuery(): Deferred = coroutineScope { + async { + client.query(NodeQuery(id = Optional.present(10))).execute().data + } + } + + suspend fun environMentSubScription(): Deferred = coroutineScope { + async { + client.subscription(EnvironmentSubscription()).toFlow().first().data + } + }*/ +} diff --git a/alchemist-webgui/src/jsMain/kotlin/Client.kt b/alchemist-webgui/src/jsMain/kotlin/Client.kt new file mode 100644 index 0000000000..dd2cfe1cb0 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/Client.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +/*import io.kvision.BootstrapCssModule +import io.kvision.BootstrapIconsModule +import io.kvision.BootstrapModule +import io.kvision.BootstrapUploadModule +import io.kvision.ChartModule +import io.kvision.CoreModule +import io.kvision.DatetimeModule +import io.kvision.FontAwesomeModule +import io.kvision.ImaskModule +import io.kvision.MapsModule +import io.kvision.RichTextModule +import io.kvision.TabulatorCssBootstrapModule +import io.kvision.TabulatorModule +import io.kvision.ToastifyModule +import io.kvision.TomSelectModule +import io.kvision.module +import io.kvision.startApplication +import ui.App + +fun main() { + startApplication( + ::App, + module.hot, + BootstrapModule, + BootstrapCssModule, + DatetimeModule, + RichTextModule, + TomSelectModule, + BootstrapUploadModule, + ImaskModule, + ToastifyModule, + FontAwesomeModule, + BootstrapIconsModule, + ChartModule, + TabulatorModule, + TabulatorCssBootstrapModule, + MapsModule, + CoreModule + ) +}*/ + +/*fun main() { + val container = document.getElementById("root") ?: error("Couldn't find container!") + createRoot(container.unsafeCast()).render(App.create()) + +}*/ diff --git a/alchemist-webgui/src/jsMain/kotlin/components/Content.kt b/alchemist-webgui/src/jsMain/kotlin/components/Content.kt new file mode 100644 index 0000000000..1144caaa90 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/components/Content.kt @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package components + +/*import ClientConnection +import io.kvision.core.* +import io.kvision.html.canvas +import io.kvision.html.div +import io.kvision.panel.SimplePanel +import io.kvision.panel.flexPanel +import io.kvision.redux.createTypedReduxStore +import io.kvision.state.bind +import io.kvision.utils.px +import it.unibo.alchemist.boundary.graphql.client.EnvironmentSubscription +import org.w3c.dom.CanvasRenderingContext2D +import stores.EnvironmentState +import stores.ScaleTranslateState +import stores.actions.ScaleTranslateAction +import stores.reducers.environmentReducer +import stores.reducers.scaleTranslateReducer +import kotlin.math.PI +import kotlin.random.Random + +open class Content() : SimplePanel(className = "content-root") { + + companion object { + const val DEFUALT_NODE_RADIUS = 0.1 + const val DEFAULT_HEIGHT = 1000 + const val DEFAULT_WIDTH = 1000 + const val DEFAULT_SCALE = 5 + const val DEFAULT_START_POSITION = 500 + const val DEFAULT_SCALE_RATIO = 0.8 + } + + val connection = ClientConnection(); + + private val store = createTypedReduxStore(::environmentReducer, EnvironmentState()) + +// private fun performGraphQLQuery(store: TypedReduxStore) { +// MainScope().launch { +// // Simulate a delayed query +// val result = connection.environMentSubScription().await() +// +// // Dispatch the result to update the Redux state +// store.dispatch(EnvironmentStateAction.SetNodes(result)) +// } +// +// } + + private fun randomColor(): String { + val letters = "0123456789ABCDEF" + var color = "#" + repeat(6) { + color += letters[Random.nextInt(15)] + } + return color + } + + private fun redraw(nodes: List?, context2D: CanvasRenderingContext2D, scale: Double = DEFAULT_SCALE.toDouble() ,translation: Pair = Pair(DEFAULT_START_POSITION.toDouble(), + DEFAULT_START_POSITION.toDouble())){ + //context2D.save() + context2D.scale(scale,scale) + context2D.translate(translation.first,translation.second) + context2D.clearRect(-translation.first,-translation.second, DEFAULT_WIDTH.toDouble(),DEFAULT_HEIGHT.toDouble()) + nodes?.forEach { + context2D.beginPath() + context2D.arc(it.position.coordinates[0], it.position.coordinates[1], DEFUALT_NODE_RADIUS, 0.0, 2 * PI, false) + context2D.fillStyle = randomColor() + context2D.fill() + context2D.closePath() + } + context2D.setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); + } + + init { + + //performGraphQLQuery(store); + + flexPanel( + io.kvision.core.FlexDirection.ROW, FlexWrap.WRAP, JustifyContent.CENTER, AlignItems.START, + spacing = 5 + ) { + + + div(className = "nodes-list").bind(store) { env -> + env.nodes?.environment?.nodeToPos?.entries?.forEach { + div { + +"position: ${it.position.coordinates}" + } + } + } + + div(className = "nodes-representation") { + + val scaleTranslationStore = createTypedReduxStore(::scaleTranslateReducer, ScaleTranslateState()) + div().bind(scaleTranslationStore){ state -> + +"Scale: ${state.scale}" + } + + div().bind(scaleTranslationStore){ state -> + +"Translation: ${state.translate.first}, ${state.translate.second}" + } + + canvas(className = "environment-renderer"){ + canvasWidth = DEFAULT_WIDTH + canvasHeight = DEFAULT_HEIGHT + border = Border(width = 1.px, style = BorderStyle.SOLID, color = Color("#ff0000")); + + var mouseIsDown = false + + var translatePos: Pair = Pair( + DEFAULT_START_POSITION.toDouble(), + DEFAULT_START_POSITION.toDouble()) + + var startDragOffset: Pair = Pair( + DEFAULT_START_POSITION.toDouble(), + DEFAULT_START_POSITION.toDouble()) + + var nodes: List? = emptyList() + + + addAfterInsertHook { + + this.bind(scaleTranslationStore) { state -> + redraw(nodes, context2D, state.scale, state.translate); + } + + this.bind(store){ env -> + //context2D = (it.elm as HTMLCanvasElement).getContext("2d") as CanvasRenderingContext2D + nodes = env.nodes?.environment?.nodeToPos?.entries + redraw( + nodes, + context2D, + scaleTranslationStore.getState().scale, + scaleTranslationStore.getState().translate) + } + + } + + onEvent { + mousedown = { e-> + cursor = Cursor.GRABBING + mouseIsDown = true + startDragOffset = Pair(e.clientX - translatePos.first, e.clientY - translatePos.second) + } + mousemove = { e-> + if(mouseIsDown){ + translatePos = Pair(e.clientX - startDragOffset.first, e.clientY - startDragOffset.second) + scaleTranslationStore.dispatch(ScaleTranslateAction.SetTranslation(translatePos)); + } + } + mouseup = { + mouseIsDown = false + cursor = Cursor.DEFAULT + } + mousewheel = { e-> + e.preventDefault() + if(e.deltaY > 0) { + scaleTranslationStore.dispatch( + ScaleTranslateAction.SetScale( + scaleTranslationStore.getState().scale * DEFAULT_SCALE_RATIO)); + }else + scaleTranslationStore.dispatch( + ScaleTranslateAction.SetScale( + scaleTranslationStore.getState().scale / DEFAULT_SCALE_RATIO)); + } + } + } + } + } + } +}*/ diff --git a/alchemist-webgui/src/jsMain/kotlin/components/Navbar.kt b/alchemist-webgui/src/jsMain/kotlin/components/Navbar.kt new file mode 100644 index 0000000000..cd13158069 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/components/Navbar.kt @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package components diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentState.kt b/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentState.kt new file mode 100644 index 0000000000..4dad498473 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentState.kt @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package stores + +/*import it.unibo.alchemist.boundary.graphql.client.EnvironmentSubscription + +data class EnvironmentState(val nodes: EnvironmentSubscription.Data? = null)*/ diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/ScaleTranslateState.kt b/alchemist-webgui/src/jsMain/kotlin/stores/ScaleTranslateState.kt new file mode 100644 index 0000000000..813bb78805 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/stores/ScaleTranslateState.kt @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package stores + +/* +import components.Content + +data class ScaleTranslateState( + var scale: Double = Content.DEFAULT_SCALE.toDouble(), + var translate: Pair = Pair((Content.DEFAULT_WIDTH /2).toDouble(),(Content.DEFAULT_HEIGHT/2).toDouble()))*/ diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/actions/EnvironmentStateAction.kt b/alchemist-webgui/src/jsMain/kotlin/stores/actions/EnvironmentStateAction.kt new file mode 100644 index 0000000000..c1f07c3344 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/stores/actions/EnvironmentStateAction.kt @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package stores.actions + +/* +import io.kvision.redux.RAction +import it.unibo.alchemist.boundary.graphql.client.EnvironmentSubscription + +sealed class EnvironmentStateAction : RAction { + data class SetNodes(val nodes: EnvironmentSubscription.Data?) : EnvironmentStateAction() +}*/ diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/actions/ScaleTranslateAction.kt b/alchemist-webgui/src/jsMain/kotlin/stores/actions/ScaleTranslateAction.kt new file mode 100644 index 0000000000..ec9287fa8d --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/stores/actions/ScaleTranslateAction.kt @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package stores.actions + +/* +import io.kvision.redux.RAction + +sealed class ScaleTranslateAction : RAction { + data class SetScale(val scale: Double) : ScaleTranslateAction() + data class SetTranslation(var translate: Pair) : ScaleTranslateAction() +}*/ diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/reducers/EnvironmentReducer.kt b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/EnvironmentReducer.kt new file mode 100644 index 0000000000..cceb4a0394 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/EnvironmentReducer.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package stores.reducers + +/* +import stores.EnvironmentState +import stores.actions.EnvironmentStateAction + +fun environmentReducer(state: EnvironmentState, action: EnvironmentStateAction): EnvironmentState = when (action) { + is EnvironmentStateAction.SetNodes -> { + state.copy(nodes = action.nodes) + } +}*/ diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/reducers/ScaleTranslateReducer.kt b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/ScaleTranslateReducer.kt new file mode 100644 index 0000000000..65972315f4 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/ScaleTranslateReducer.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package stores.reducers + +/*import stores.ScaleTranslateState +import stores.actions.ScaleTranslateAction + +fun scaleTranslateReducer(state: ScaleTranslateState, action: ScaleTranslateAction): ScaleTranslateState = when (action) { + is ScaleTranslateAction.SetScale -> { + state.copy(scale = action.scale) + } + + is ScaleTranslateAction.SetTranslation -> { + state.copy(translate = action.translate) + } +}*/ diff --git a/alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt b/alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt new file mode 100644 index 0000000000..f961ea4632 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package ui + +/*import components.Content +import io.kvision.Application +import io.kvision.panel.root +import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.HTMLCanvasElement +import web.dom.document +import kotlin.math.PI*/ + +/*val connection = ClientConnection(); +private val scope = MainScope() +private const val UPDATE_STATE_DELAY: Long = 100 + +val App = FC { + val (data, setData) = useState(null) + + useEffectOnce { + scope.launch { + val result = connection.retrieveQuery().await() + setData(result) + } + } + div { + h1 { + +"The result of the query:" + } + + h2 { + +NodeQuery.OPERATION_DOCUMENT + } + // Render data from GraphQL query + div { + +"Position is:${data?.nodePosition?.coordinates}" + } + } +}*/ + +/*class App : Application() { + override fun start() { + root("root") { + add(Content()) + } + } +}*/ diff --git a/alchemist-webgui/src/jsMain/resources/index.html b/alchemist-webgui/src/jsMain/resources/index.html new file mode 100644 index 0000000000..d89ca0e268 --- /dev/null +++ b/alchemist-webgui/src/jsMain/resources/index.html @@ -0,0 +1,21 @@ + + + + + + + + Alchemist Web Interface + + + +
+ + \ No newline at end of file diff --git a/alchemist-webgui/webpack.config.d/bootstrap.js b/alchemist-webgui/webpack.config.d/bootstrap.js new file mode 100644 index 0000000000..c9ac06dc31 --- /dev/null +++ b/alchemist-webgui/webpack.config.d/bootstrap.js @@ -0,0 +1,3 @@ +config.module.rules.push({test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, type: 'asset'}); +config.module.rules.push({test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, type: 'asset'}); +config.module.rules.push({test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, type: 'asset/resource'}); diff --git a/alchemist-webgui/webpack.config.d/css.js b/alchemist-webgui/webpack.config.d/css.js new file mode 100644 index 0000000000..3ed3ebc15d --- /dev/null +++ b/alchemist-webgui/webpack.config.d/css.js @@ -0,0 +1,2 @@ +config.module.rules.push({ test: /\.css$/, use: ["style-loader", { loader: "css-loader", options: {sourceMap: false} } ] }); + diff --git a/alchemist-webgui/webpack.config.d/file.js b/alchemist-webgui/webpack.config.d/file.js new file mode 100644 index 0000000000..aa64925a0a --- /dev/null +++ b/alchemist-webgui/webpack.config.d/file.js @@ -0,0 +1,6 @@ +config.module.rules.push( + { + test: /\.(jpe?g|png|gif|svg)$/i, + type: 'asset/resource' + } +); diff --git a/alchemist-webgui/webpack.config.d/handlebars.js b/alchemist-webgui/webpack.config.d/handlebars.js new file mode 100644 index 0000000000..d17c90e6c7 --- /dev/null +++ b/alchemist-webgui/webpack.config.d/handlebars.js @@ -0,0 +1,6 @@ +config.module.rules.push( + { + test: /\.hbs$/i, + loader: 'handlebars-loader' + } +); diff --git a/alchemist-webgui/webpack.config.d/webpack.js b/alchemist-webgui/webpack.config.d/webpack.js new file mode 100644 index 0000000000..c180514d02 --- /dev/null +++ b/alchemist-webgui/webpack.config.d/webpack.js @@ -0,0 +1,17 @@ +config.resolve.modules.push("../../processedResources/js/main"); +config.resolve.conditionNames = ['import', 'require', 'default']; + +if (config.devServer) { + config.devServer.hot = true; + config.devServer.compress = false; // workaround for SSE + config.devtool = 'eval-cheap-source-map'; +} else { + config.devtool = undefined; +} + +// disable bundle size warning +config.performance = { + assetFilter: function (assetFilename) { + return !assetFilename.endsWith('.js'); + }, +}; diff --git a/javadoc-io.json b/javadoc-io.json index b421c923cf..85ad727e70 100644 --- a/javadoc-io.json +++ b/javadoc-io.json @@ -43,6 +43,20 @@ "first": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.8", "second": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.8/element-list" }, + "com.apollographql.apollo3/apollo-runtime/4.0.0-beta.4": { + "first": "https://javadoc.io/doc/com.apollographql.apollo3/apollo-runtime/4.0.0-beta.4" + }, + "com.expediagroup/graphql-kotlin-hooks-provider/7.0.2": { + "first": "https://javadoc.io/doc/com.expediagroup/graphql-kotlin-hooks-provider/7.0.2" + }, + "com.expediagroup/graphql-kotlin-ktor-server/7.0.2": { + "first": "https://javadoc.io/doc/com.expediagroup/graphql-kotlin-ktor-server/7.0.2", + "second": "https://javadoc.io/doc/com.expediagroup/graphql-kotlin-ktor-server/7.0.2/package-list" + }, + "com.expediagroup/graphql-kotlin-server/7.0.2": { + "first": "https://javadoc.io/doc/com.expediagroup/graphql-kotlin-server/7.0.2", + "second": "https://javadoc.io/doc/com.expediagroup/graphql-kotlin-server/7.0.2/package-list" + }, "com.github.ben-manes.caffeine/caffeine/3.0.5": { "first": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.0.5", "second": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.0.5/element-list" @@ -149,6 +163,10 @@ "first": "https://javadoc.io/doc/com.google.guava/guava/32.1.3-jre", "second": "https://javadoc.io/doc/com.google.guava/guava/32.1.3-jre/element-list" }, + "com.google.guava/guava/33.0.0-jre": { + "first": "https://javadoc.io/doc/com.google.guava/guava/33.0.0-jre", + "second": "https://javadoc.io/doc/com.google.guava/guava/33.0.0-jre/element-list" + }, "com.graphhopper/graphhopper-core/4.0": { "first": "https://javadoc.io/doc/com.graphhopper/graphhopper-core/4.0", "second": "https://javadoc.io/doc/com.graphhopper/graphhopper-core/4.0/package-list" @@ -203,6 +221,10 @@ "first": "https://javadoc.io/doc/com.miglayout/miglayout-swing/11.2", "second": "https://javadoc.io/doc/com.miglayout/miglayout-swing/11.2/element-list" }, + "com.miglayout/miglayout-swing/11.3": { + "first": "https://javadoc.io/doc/com.miglayout/miglayout-swing/11.3", + "second": "https://javadoc.io/doc/com.miglayout/miglayout-swing/11.3/element-list" + }, "com.uchuhimo/konf/1.1.2": { "first": "https://javadoc.io/doc/com.uchuhimo/konf/1.1.2" }, @@ -336,6 +358,9 @@ "first": "https://javadoc.io/doc/io.jenetics/jpx/3.1.0", "second": "https://javadoc.io/doc/io.jenetics/jpx/3.1.0/element-list" }, + "io.ktor/ktor-bom/2.3.7": { + "first": "https://javadoc.io/doc/io.ktor/ktor-bom/2.3.7" + }, "it.unibo.scafi/scafi-core_2.13/1.1.7": { "first": "https://javadoc.io/doc/it.unibo.scafi/scafi-core_2.13/1.1.7" }, @@ -409,6 +434,10 @@ "first": "https://javadoc.io/doc/org.apache.ignite/ignite-core/2.15.0", "second": "https://javadoc.io/doc/org.apache.ignite/ignite-core/2.15.0/package-list" }, + "org.apache.ignite/ignite-core/2.16.0": { + "first": "https://javadoc.io/doc/org.apache.ignite/ignite-core/2.16.0", + "second": "https://javadoc.io/doc/org.apache.ignite/ignite-core/2.16.0/package-list" + }, "org.apache.ignite/ignite-indexing/2.12.0": { "first": "https://javadoc.io/doc/org.apache.ignite/ignite-indexing/2.12.0", "second": "https://javadoc.io/doc/org.apache.ignite/ignite-indexing/2.12.0/package-list" @@ -424,6 +453,10 @@ "first": "https://javadoc.io/doc/org.apache.ignite/ignite-indexing/2.15.0", "second": "https://javadoc.io/doc/org.apache.ignite/ignite-indexing/2.15.0/package-list" }, + "org.apache.ignite/ignite-indexing/2.16.0": { + "first": "https://javadoc.io/doc/org.apache.ignite/ignite-indexing/2.16.0", + "second": "https://javadoc.io/doc/org.apache.ignite/ignite-indexing/2.16.0/package-list" + }, "org.apache.ignite/ignite-spring/2.12.0": { "first": "https://javadoc.io/doc/org.apache.ignite/ignite-spring/2.12.0", "second": "https://javadoc.io/doc/org.apache.ignite/ignite-spring/2.12.0/package-list" @@ -439,6 +472,10 @@ "first": "https://javadoc.io/doc/org.apache.ignite/ignite-spring/2.15.0", "second": "https://javadoc.io/doc/org.apache.ignite/ignite-spring/2.15.0/package-list" }, + "org.apache.ignite/ignite-spring/2.16.0": { + "first": "https://javadoc.io/doc/org.apache.ignite/ignite-spring/2.16.0", + "second": "https://javadoc.io/doc/org.apache.ignite/ignite-spring/2.16.0/package-list" + }, "org.controlsfx/controlsfx/11.1.1": { "first": "https://javadoc.io/doc/org.controlsfx/controlsfx/11.1.1", "second": "https://javadoc.io/doc/org.controlsfx/controlsfx/11.1.1/element-list" @@ -500,6 +537,10 @@ "first": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.16", "second": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.16/element-list" }, + "org.danilopianini/jirf/0.4.17": { + "first": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.17", + "second": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.17/element-list" + }, "org.danilopianini/jirf/0.4.5": { "first": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.5", "second": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.5/element-list" @@ -579,6 +620,9 @@ "org.jetbrains.kotlin/kotlin-reflect/1.9.21": { "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-reflect/1.9.21" }, + "org.jetbrains.kotlin/kotlin-reflect/1.9.22": { + "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-reflect/1.9.22" + }, "org.jetbrains.kotlin/kotlin-reflect/null": { "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-reflect/null" }, @@ -621,9 +665,15 @@ "org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.9.21": { "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.9.21" }, + "org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.9.22": { + "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.9.22" + }, "org.jetbrains.kotlin/kotlin-stdlib-jdk8/null": { "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib-jdk8/null" }, + "org.jetbrains.kotlin/kotlin-stdlib/1.9.22": { + "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib/1.9.22" + }, "org.jetbrains.kotlinx/kotlinx-cli/0.3.5": { "first": "https://javadoc.io/doc/org.jetbrains.kotlinx/kotlinx-cli/0.3.5" }, @@ -654,6 +704,9 @@ "org.jetbrains.kotlinx/kotlinx-coroutines-core/1.7.3": { "first": "https://javadoc.io/doc/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.7.3" }, + "org.jetbrains.kotlinx/kotlinx-serialization-json/1.6.2": { + "first": "https://javadoc.io/doc/org.jetbrains.kotlinx/kotlinx-serialization-json/1.6.2" + }, "org.jgrapht/jgrapht-core/1.5.1": { "first": "https://javadoc.io/doc/org.jgrapht/jgrapht-core/1.5.1", "second": "https://javadoc.io/doc/org.jgrapht/jgrapht-core/1.5.1/element-list" @@ -775,6 +828,10 @@ "first": "https://javadoc.io/doc/org.protelis/protelis-interpreter/17.3.5", "second": "https://javadoc.io/doc/org.protelis/protelis-interpreter/17.3.5/element-list" }, + "org.protelis/protelis-interpreter/17.3.9": { + "first": "https://javadoc.io/doc/org.protelis/protelis-interpreter/17.3.9", + "second": "https://javadoc.io/doc/org.protelis/protelis-interpreter/17.3.9/element-list" + }, "org.protelis/protelis-lang/17.0.0": { "first": "https://javadoc.io/doc/org.protelis/protelis-lang/17.0.0", "second": "https://javadoc.io/doc/org.protelis/protelis-lang/17.0.0/package-list" @@ -787,6 +844,10 @@ "first": "https://javadoc.io/doc/org.protelis/protelis-lang/17.3.5", "second": "https://javadoc.io/doc/org.protelis/protelis-lang/17.3.5/package-list" }, + "org.protelis/protelis-lang/17.3.9": { + "first": "https://javadoc.io/doc/org.protelis/protelis-lang/17.3.9", + "second": "https://javadoc.io/doc/org.protelis/protelis-lang/17.3.9/package-list" + }, "org.scala-lang/scala-compiler/2.13.10": { "first": "https://javadoc.io/doc/org.scala-lang/scala-compiler/2.13.10" }, @@ -837,6 +898,10 @@ "first": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.1", "second": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.1/element-list" }, + "org.slf4j/slf4j-api/2.0.10": { + "first": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.10", + "second": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.10/element-list" + }, "org.slf4j/slf4j-api/2.0.2": { "first": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.2", "second": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.2/element-list" diff --git a/settings.gradle.kts b/settings.gradle.kts index 2664ae4bb7..309827259a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -41,6 +41,7 @@ include( "alchemist-swingui", "alchemist-fxui", "alchemist-web-renderer", + "alchemist-webgui", ) rootProject.name = "alchemist" From 9d79e5fc258bfc751866b838f355072eaa961bb0 Mon Sep 17 00:00:00 2001 From: Tiziano Vuksan Date: Mon, 29 Jan 2024 18:07:23 +0100 Subject: [PATCH 02/22] feat(webgui): added nodes subscription rendering nodes now are represented in a canvas context --- alchemist-webgui/build.gradle.kts | 64 ++++- .../src/commonMain/kotlin/ClientConnection.kt | 40 --- .../kotlin/graphql/api/EnvironmentApi.kt | 34 +++ .../graphql/api/SimulationControlApi.kt | 57 ++++ .../src/commonMain/kotlin/utils/SimState.kt | 51 ++++ alchemist-webgui/src/jsMain/kotlin/Client.kt | 6 +- .../src/jsMain/kotlin/components/Appbar.kt | 51 ++++ .../src/jsMain/kotlin/components/Content.kt | 172 ++---------- .../components/content/SimulationContext.kt | 255 ++++++++++++++++++ .../{Navbar.kt => navbar/NavTitle.kt} | 4 +- .../kotlin/components/navbar/PlayButton.kt | 79 ++++++ .../jsMain/kotlin/stores/EnvironmentStore.kt | 34 +++ .../jsMain/kotlin/stores/SimulationStatus.kt | 32 +++ .../stores/actions/EnvironmentStateAction.kt | 6 +- .../stores/actions/ScaleTranslateAction.kt | 3 +- .../kotlin/stores/actions/SimulationAction.kt | 17 ++ .../stores/reducers/EnvironmentReducer.kt | 19 +- .../stores/reducers/ScaleTranslateReducer.kt | 4 +- .../stores/reducers/SimulationReducer.kt | 19 ++ .../src/jsMain/kotlin/ui/AppMain.kt | 40 +-- settings.gradle.kts | 4 +- 21 files changed, 738 insertions(+), 253 deletions(-) delete mode 100644 alchemist-webgui/src/commonMain/kotlin/ClientConnection.kt create mode 100644 alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt create mode 100644 alchemist-webgui/src/commonMain/kotlin/graphql/api/SimulationControlApi.kt create mode 100644 alchemist-webgui/src/commonMain/kotlin/utils/SimState.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt rename alchemist-webgui/src/jsMain/kotlin/components/{Navbar.kt => navbar/NavTitle.kt} (89%) create mode 100644 alchemist-webgui/src/jsMain/kotlin/components/navbar/PlayButton.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/stores/SimulationStatus.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/stores/actions/SimulationAction.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/stores/reducers/SimulationReducer.kt diff --git a/alchemist-webgui/build.gradle.kts b/alchemist-webgui/build.gradle.kts index 6a884b4fbe..c587c4ce64 100644 --- a/alchemist-webgui/build.gradle.kts +++ b/alchemist-webgui/build.gradle.kts @@ -1,6 +1,6 @@ import Libs.alchemist +import io.gitlab.arturbosch.detekt.Detekt import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile group = "it.unibo.alchemist" val alchemistGroup = "Run Alchemist" @@ -13,7 +13,8 @@ plugins { alias(libs.plugins.ktor) alias(libs.plugins.graphql.server) alias(libs.plugins.graphql.client) - // id("io.kvision") version "7.3.1" + + id("io.kvision") version "7.3.1" } repositories { @@ -21,6 +22,23 @@ repositories { mavenLocal() } +dependencies { + implementation(alchemist("graphql")) + implementation(alchemist("api")) + implementation(alchemist("full")) + + implementation(libs.apollo.runtime) + implementation(libs.kotlin.coroutines.core) + implementation(libs.kotlinx.serialization.json) + + implementation(libs.ktor.server.websockets) + implementation(libs.bundles.graphql.server) + implementation(libs.bundles.ktor.server) + + implementation(libs.kotest.runner) + implementation(libs.ktor.server.test.host) +} + application { mainClass.set("it.unibo.alchemist.Alchemist") } @@ -35,16 +53,31 @@ val task by tasks.register("runWEB") { // Set the property value from the command line or use a default value simulationFile.set(project.findProperty("simulationFile") as String? ?: "C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/Alchemist fork/Alchemist/test.yml") // effect.set(project.findProperty("effect") as String? ?: "") + mainClass.set("it.unibo.alchemist.Alchemist") // Add the simulation file path as a program argument args("run", "C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/yaml/" + simulationFile.get()) - classpath(tasks.named("compileKotlinJvm"), configurations.named("jvmRuntimeClasspath")) + doFirst { + classpath( + sourceSets["main"].runtimeClasspath, + // tasks.named("compileKotlinJvm"), + // configurations.named("jvmRuntimeClasspath"), + files("C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/Alchemist fork/Alchemist/alchemist-graphql/build/libs/alchemist-graphql-0.1.0-archeo+343bdac0b.jar"), + files("C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/Alchemist fork/Alchemist/alchemist-graphql/build/libs/alchemist-graphql-js-0.1.0-archeo+343bdac0b.klib"), + files("C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/Alchemist fork/Alchemist/alchemist-graphql/build/libs/alchemist-graphql-jvm-0.1.0-archeo+343bdac0b.jar"), + files("C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/Alchemist fork/Alchemist/alchemist-graphql/build/libs/alchemist-graphql-kotlin-0.1.0-archeo+343bdac0b-sources.jar"), + files("C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/Alchemist fork/Alchemist/alchemist-graphql/build/libs/alchemist-graphql-metadata-0.1.0-archeo+343bdac0b.jar"), + + ) + } + + // classpath(tasks.named("compileKotlinJvm"), configurations.named("jvmRuntimeClasspath")) // , "-g","C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/effects/" + effect.get() } -tasks { +/*tasks { named("run", JavaExec::class).configure { classpath(named("compileKotlinJvm"), configurations.named("jvmRuntimeClasspath")) // val simulationFile = project.objects.property(String::class.java) @@ -60,7 +93,7 @@ tasks.withType().configureEach { tasks.named("sourcesJar").configure { dependsOn("kspCommonMainKotlinMetadata") -} +}*/ kotlin { jvm { @@ -102,7 +135,6 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation(alchemist("web-renderer")) implementation(alchemist("graphql")) implementation(libs.apollo.runtime) implementation(libs.korim) @@ -133,7 +165,7 @@ kotlin { } }*/ - val jvmMain by getting { + /*val jvmMain by getting { dependencies { implementation(alchemist("graphql")) implementation(libs.bundles.ktor.server) @@ -146,7 +178,7 @@ kotlin { implementation(libs.ktor.client.core) implementation(libs.redux.kotlin.threadsafe) } - } + }*/ val jsMain by getting { dependencies { @@ -155,7 +187,7 @@ kotlin { implementation(libs.bundles.ktor.client) implementation(libs.bundles.kotlin.react) - /*implementation("io.kvision:kvision:$kvisionVersion") + implementation("io.kvision:kvision:$kvisionVersion") implementation("io.kvision:kvision-bootstrap:$kvisionVersion") implementation("io.kvision:kvision-datetime:$kvisionVersion") implementation("io.kvision:kvision-richtext:$kvisionVersion") @@ -176,7 +208,7 @@ kotlin { implementation("io.kvision:kvision-state:$kvisionVersion") implementation("io.kvision:kvision-state-flow:$kvisionVersion") implementation("io.kvision:kvision-ballast:$kvisionVersion") - implementation("io.kvision:kvision-redux-kotlin:$kvisionVersion")*/ + implementation("io.kvision:kvision-redux-kotlin:$kvisionVersion") } } @@ -207,3 +239,15 @@ kotlin { }*/ } } + +fun PatternFilterable.excludeGenerated() = exclude { "build${File.separator}generated" in it.file.absolutePath } +tasks.withType().configureEach { excludeGenerated() } +ktlint { filter { excludeGenerated() } } + +tasks.named("runKtlintFormatOverCommonMainSourceSet").configure { + dependsOn(tasks.named("kspCommonMainKotlinMetadata")) +} + +tasks.named("runKtlintCheckOverCommonMainSourceSet").configure { + dependsOn(tasks.named("kspCommonMainKotlinMetadata")) +} diff --git a/alchemist-webgui/src/commonMain/kotlin/ClientConnection.kt b/alchemist-webgui/src/commonMain/kotlin/ClientConnection.kt deleted file mode 100644 index b531627cd1..0000000000 --- a/alchemist-webgui/src/commonMain/kotlin/ClientConnection.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2010-2024, Danilo Pianini and contributors - * listed, for each module, in the respective subproject's build.gradle.kts file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -/*import com.apollographql.apollo3.api.Optional -import it.unibo.alchemist.boundary.graphql.client.GraphQLClient -import it.unibo.alchemist.boundary.graphql.client.GraphQLClientFactory -import it.unibo.alchemist.boundary.graphql.client.EnvironmentSubscription -import it.unibo.alchemist.boundary.graphql.client.GraphQLClient -import it.unibo.alchemist.boundary.graphql.client.GraphQLClientFactory -import it.unibo.alchemist.boundary.graphql.client.NodeQuery -import it.unibo.alchemist.boundary.webui.common.model.surrogate.Position2DSurrogate -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.first*/ - -open class ClientConnection() { - - // private val client: GraphQLClient = GraphQLClientFactory.subscriptionClient() - - // private val pos: Position2DSurrogate = Position2DSurrogate(5.0,6.0) - - /*suspend fun retrieveQuery(): Deferred = coroutineScope { - async { - client.query(NodeQuery(id = Optional.present(10))).execute().data - } - } - - suspend fun environMentSubScription(): Deferred = coroutineScope { - async { - client.subscription(EnvironmentSubscription()).toFlow().first().data - } - }*/ -} diff --git a/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt b/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt new file mode 100644 index 0000000000..57c1599a5d --- /dev/null +++ b/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package graphql.api + +import client.ClientConnection +import com.apollographql.apollo3.api.ApolloResponse +import com.apollographql.apollo3.api.Optional +import it.unibo.alchemist.boundary.graphql.client.EnvironmentSubscription +import it.unibo.alchemist.boundary.graphql.client.NodeQuery +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.Flow + +object EnvironmentApi { + + suspend fun retrieveQuery(): Deferred = coroutineScope { + async { + ClientConnection.client.query(NodeQuery(id = Optional.present(10))).execute().data + } + } + + fun environMentSubScription(): Flow> { + // ClientConnection.client.close() + return ClientConnection.client.subscription(EnvironmentSubscription()).toFlow() + } +} diff --git a/alchemist-webgui/src/commonMain/kotlin/graphql/api/SimulationControlApi.kt b/alchemist-webgui/src/commonMain/kotlin/graphql/api/SimulationControlApi.kt new file mode 100644 index 0000000000..524723eceb --- /dev/null +++ b/alchemist-webgui/src/commonMain/kotlin/graphql/api/SimulationControlApi.kt @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package graphql.api + +import client.ClientConnection +import it.unibo.alchemist.boundary.graphql.client.PauseSimulationMutation +import it.unibo.alchemist.boundary.graphql.client.PlaySimulationMutation +import it.unibo.alchemist.boundary.graphql.client.SimulationStatusQuery +import it.unibo.alchemist.boundary.graphql.client.TerminateSimulationMutation +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.withContext + +object SimulationControlApi { + + suspend fun getSimulationStatus(): Deferred = coroutineScope { + async { + ClientConnection.client.query(SimulationStatusQuery()).execute().data + } + } + + suspend fun pauseSimulation() = coroutineScope { + async { + withContext(Dispatchers.Default) { + println("paused the simulation") + ClientConnection.client.mutation(PauseSimulationMutation()).execute() + } + } + } + + suspend fun playSimulation() = coroutineScope { + async { + withContext(Dispatchers.Default) { + println("playing the simulation") + ClientConnection.client.mutation(PlaySimulationMutation()).execute() + } + } + } + + suspend fun terminateSimulation() = coroutineScope { + async { + withContext(Dispatchers.Default) { + println("terminated the simulation") + ClientConnection.client.mutation(TerminateSimulationMutation()).execute() + } + } + } +} diff --git a/alchemist-webgui/src/commonMain/kotlin/utils/SimState.kt b/alchemist-webgui/src/commonMain/kotlin/utils/SimState.kt new file mode 100644 index 0000000000..f5c9508a99 --- /dev/null +++ b/alchemist-webgui/src/commonMain/kotlin/utils/SimState.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package utils + +enum class SimState { + /** + * The simulation is being initialized. + */ + INIT, + + /** + * The simulation is ready to be started. + */ + READY, + + /** + * The simulation is paused. It can be resumed. + */ + PAUSED, + + /** + * The simulation is currently running. + */ + RUNNING, + + /** + * The simulation is stopped. It is no longer possible to resume + * it. + */ + TERMINATED, ; + + companion object { + fun toSimStatus(value: String?): SimState { + return when (value) { + "INIT" -> SimState.INIT + "READY" -> SimState.READY + "PAUSED" -> SimState.PAUSED + "RUNNING" -> SimState.RUNNING + "TERMINATED" -> SimState.TERMINATED + else -> SimState.TERMINATED // Default to a reasonable value when the string is null or unknown + } + } + } +} diff --git a/alchemist-webgui/src/jsMain/kotlin/Client.kt b/alchemist-webgui/src/jsMain/kotlin/Client.kt index dd2cfe1cb0..7714f0e924 100644 --- a/alchemist-webgui/src/jsMain/kotlin/Client.kt +++ b/alchemist-webgui/src/jsMain/kotlin/Client.kt @@ -7,7 +7,7 @@ * as described in the file LICENSE in the Alchemist distribution's top directory. */ -/*import io.kvision.BootstrapCssModule +import io.kvision.BootstrapCssModule import io.kvision.BootstrapIconsModule import io.kvision.BootstrapModule import io.kvision.BootstrapUploadModule @@ -44,9 +44,9 @@ fun main() { TabulatorModule, TabulatorCssBootstrapModule, MapsModule, - CoreModule + CoreModule, ) -}*/ +} /*fun main() { val container = document.getElementById("root") ?: error("Couldn't find container!") diff --git a/alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt b/alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt new file mode 100644 index 0000000000..daa271d94b --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package components + +import components.navbar.PlayButton +import io.kvision.core.Color +import io.kvision.html.div +import io.kvision.navbar.NavbarColor +import io.kvision.navbar.NavbarType +import io.kvision.navbar.nav +import io.kvision.navbar.navbar +import io.kvision.panel.SimplePanel +import io.kvision.state.bind +import stores.SimulationStatus + +open class Appbar() : SimplePanel(className = "appbar-root") { + + private val simulationFileName = "simulationTest.yml" + + init { + // SimulationStatus.callGetStatus() + navbar( + if (simulationFileName.isEmpty()) "Alchemist" else "Alchemist - $simulationFileName", + collapseOnClick = false, + ) { + type = NavbarType.STICKYTOP + nColor = NavbarColor.DARK + + nav { + + div { + color = Color("#ffffff") + }.bind(SimulationStatus.simulationStore) { sim -> + val simStatus = sim.status?.simulationStatus + +"SIMULATION STATUS IS: $simStatus" + } + } + + nav(rightAlign = true) { + add(PlayButton("Play")) + } + } + } +} diff --git a/alchemist-webgui/src/jsMain/kotlin/components/Content.kt b/alchemist-webgui/src/jsMain/kotlin/components/Content.kt index 1144caaa90..dc52bac8a0 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/Content.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/Content.kt @@ -9,171 +9,45 @@ package components -/*import ClientConnection -import io.kvision.core.* -import io.kvision.html.canvas +import components.content.SimulationContext +import io.kvision.core.AlignItems +import io.kvision.core.FlexDirection +import io.kvision.core.FlexWrap +import io.kvision.core.JustifyContent import io.kvision.html.div import io.kvision.panel.SimplePanel import io.kvision.panel.flexPanel -import io.kvision.redux.createTypedReduxStore import io.kvision.state.bind -import io.kvision.utils.px -import it.unibo.alchemist.boundary.graphql.client.EnvironmentSubscription -import org.w3c.dom.CanvasRenderingContext2D -import stores.EnvironmentState -import stores.ScaleTranslateState -import stores.actions.ScaleTranslateAction -import stores.reducers.environmentReducer -import stores.reducers.scaleTranslateReducer -import kotlin.math.PI -import kotlin.random.Random +import stores.EnvironmentStore open class Content() : SimplePanel(className = "content-root") { - companion object { - const val DEFUALT_NODE_RADIUS = 0.1 - const val DEFAULT_HEIGHT = 1000 - const val DEFAULT_WIDTH = 1000 - const val DEFAULT_SCALE = 5 - const val DEFAULT_START_POSITION = 500 - const val DEFAULT_SCALE_RATIO = 0.8 - } - - val connection = ClientConnection(); - - private val store = createTypedReduxStore(::environmentReducer, EnvironmentState()) - -// private fun performGraphQLQuery(store: TypedReduxStore) { -// MainScope().launch { -// // Simulate a delayed query -// val result = connection.environMentSubScription().await() -// -// // Dispatch the result to update the Redux state -// store.dispatch(EnvironmentStateAction.SetNodes(result)) -// } -// -// } - - private fun randomColor(): String { - val letters = "0123456789ABCDEF" - var color = "#" - repeat(6) { - color += letters[Random.nextInt(15)] - } - return color - } - - private fun redraw(nodes: List?, context2D: CanvasRenderingContext2D, scale: Double = DEFAULT_SCALE.toDouble() ,translation: Pair = Pair(DEFAULT_START_POSITION.toDouble(), - DEFAULT_START_POSITION.toDouble())){ - //context2D.save() - context2D.scale(scale,scale) - context2D.translate(translation.first,translation.second) - context2D.clearRect(-translation.first,-translation.second, DEFAULT_WIDTH.toDouble(),DEFAULT_HEIGHT.toDouble()) - nodes?.forEach { - context2D.beginPath() - context2D.arc(it.position.coordinates[0], it.position.coordinates[1], DEFUALT_NODE_RADIUS, 0.0, 2 * PI, false) - context2D.fillStyle = randomColor() - context2D.fill() - context2D.closePath() - } - context2D.setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); - } - init { - //performGraphQLQuery(store); + // EnvironmentStore.callEnvironmentSubscription() + + // val offcanvas = offcanvas("Lorem ipsum", OffPlacement.END, dark = true) flexPanel( - io.kvision.core.FlexDirection.ROW, FlexWrap.WRAP, JustifyContent.CENTER, AlignItems.START, - spacing = 5 + FlexDirection.ROW, + FlexWrap.WRAP, + JustifyContent.CENTER, + AlignItems.START, + spacing = 5, ) { + div(className = "nodes-list") { - - div(className = "nodes-list").bind(store) { env -> - env.nodes?.environment?.nodeToPos?.entries?.forEach { - div { - +"position: ${it.position.coordinates}" + bind(EnvironmentStore.store) { env -> + env.nodes.forEach { + div { +"position: ${it.position.coordinates}" } } } } - div(className = "nodes-representation") { - - val scaleTranslationStore = createTypedReduxStore(::scaleTranslateReducer, ScaleTranslateState()) - div().bind(scaleTranslationStore){ state -> - +"Scale: ${state.scale}" - } - - div().bind(scaleTranslationStore){ state -> - +"Translation: ${state.translate.first}, ${state.translate.second}" - } - - canvas(className = "environment-renderer"){ - canvasWidth = DEFAULT_WIDTH - canvasHeight = DEFAULT_HEIGHT - border = Border(width = 1.px, style = BorderStyle.SOLID, color = Color("#ff0000")); - - var mouseIsDown = false - - var translatePos: Pair = Pair( - DEFAULT_START_POSITION.toDouble(), - DEFAULT_START_POSITION.toDouble()) - - var startDragOffset: Pair = Pair( - DEFAULT_START_POSITION.toDouble(), - DEFAULT_START_POSITION.toDouble()) - - var nodes: List? = emptyList() - - - addAfterInsertHook { - - this.bind(scaleTranslationStore) { state -> - redraw(nodes, context2D, state.scale, state.translate); - } - - this.bind(store){ env -> - //context2D = (it.elm as HTMLCanvasElement).getContext("2d") as CanvasRenderingContext2D - nodes = env.nodes?.environment?.nodeToPos?.entries - redraw( - nodes, - context2D, - scaleTranslationStore.getState().scale, - scaleTranslationStore.getState().translate) - } - - } - - onEvent { - mousedown = { e-> - cursor = Cursor.GRABBING - mouseIsDown = true - startDragOffset = Pair(e.clientX - translatePos.first, e.clientY - translatePos.second) - } - mousemove = { e-> - if(mouseIsDown){ - translatePos = Pair(e.clientX - startDragOffset.first, e.clientY - startDragOffset.second) - scaleTranslationStore.dispatch(ScaleTranslateAction.SetTranslation(translatePos)); - } - } - mouseup = { - mouseIsDown = false - cursor = Cursor.DEFAULT - } - mousewheel = { e-> - e.preventDefault() - if(e.deltaY > 0) { - scaleTranslationStore.dispatch( - ScaleTranslateAction.SetScale( - scaleTranslationStore.getState().scale * DEFAULT_SCALE_RATIO)); - }else - scaleTranslationStore.dispatch( - ScaleTranslateAction.SetScale( - scaleTranslationStore.getState().scale / DEFAULT_SCALE_RATIO)); - } - } - } - } + val simulationContext = SimulationContext() + add(simulationContext) } + + // offcanvas.show() } -}*/ +} diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt new file mode 100644 index 0000000000..9d851348e0 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package components.content + +import graphql.api.EnvironmentApi +import io.kvision.core.AlignItems +import io.kvision.core.Border +import io.kvision.core.BorderStyle +import io.kvision.core.Color +import io.kvision.core.Cursor +import io.kvision.core.FlexWrap +import io.kvision.core.JustifyContent +import io.kvision.core.onEvent +import io.kvision.core.onInput +import io.kvision.form.number.range +import io.kvision.html.canvas +import io.kvision.html.div +import io.kvision.panel.SimplePanel +import io.kvision.panel.hPanel +import io.kvision.panel.vPanel +import io.kvision.progress.progress +import io.kvision.redux.createTypedReduxStore +import io.kvision.state.ObservableValue +import io.kvision.state.bind +import io.kvision.utils.px +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import org.w3c.dom.CanvasRenderingContext2D +import stores.EnvironmentStore +import stores.actions.EnvironmentStateAction +import stores.actions.ScaleTranslateAction +import stores.reducers.scaleTranslateReducer +import stores.states.ScaleTranslateState +import kotlin.math.PI +import kotlin.math.min +import kotlin.random.Random + +class SimulationContext : SimplePanel(className = "simulation-context") { + + companion object { + const val DEFUALT_NODE_RADIUS = 5.0 + const val DEFAULT_HEIGHT = 1000 + const val DEFAULT_WIDTH = 1000 + const val DEFAULT_SCALE = 25 + const val DEFAULT_START_POSITION = 0 + const val DEFAULT_SCALE_RATIO = 0.8 + const val DEFAULT_NODE_COLOR = "#FF0000" + } + private val scaleTranslationStore = createTypedReduxStore(::scaleTranslateReducer, ScaleTranslateState()) + private var nodesRadius: ObservableValue = ObservableValue(DEFUALT_NODE_RADIUS) + + private fun randomColor(): String { + val letters = "0123456789ABCDEF" + var color = "#" + repeat(6) { color += letters[Random.nextInt(15)] } + return color + } + + private fun CanvasRenderingContext2D.drawNode(position: Pair, color: String = DEFAULT_NODE_COLOR) { + beginPath() + arc( + position.first, + position.second, + nodesRadius.value * 1 / scaleTranslationStore.getState().scale, + 0.0, + 2 * PI, + false, + ) + fillStyle = color + fill() + closePath() + } + + private fun CanvasRenderingContext2D.redrawNodes( + scale: Double = DEFAULT_SCALE.toDouble(), + translation: Pair = + Pair(DEFAULT_START_POSITION.toDouble(), DEFAULT_START_POSITION.toDouble()), + ) { + println("Redrawing function") + val translationScaled = Pair( + translation.first * 1 / scaleTranslationStore.getState().scale, + translation.second * 1 / scaleTranslationStore.getState().scale, + ) + + scale(scale, scale) + translate(translationScaled.first, translationScaled.second) + + clearRect( + -translationScaled.first, + -translationScaled.second, + DEFAULT_WIDTH.toDouble(), + DEFAULT_HEIGHT.toDouble(), + ) + + // println("Translated to:"+translationScaled.first+", "+translationScaled.second) + + EnvironmentStore.store.getState().nodes.forEach { + drawNode(Pair(it.position.coordinates[0], it.position.coordinates[1]), randomColor()) + } + + setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) + } + + init { + /*CoroutineScope(Dispatchers.CIO).launch { + println("Init scope update environment store") + EnvironmentApi.environMentSubScription().collect { response -> + //async{ + println("data-->"+response.data) + //}.await() + + //EnvironmentStore.store.dispatch(EnvironmentStateAction.SetNodes(it.data?.environment?.nodeToPos!!.entries.toMutableList())) + } + }*/ + + CoroutineScope(Dispatchers.Default).launch { + EnvironmentApi.environMentSubScription().collect { + // println("A") + EnvironmentStore.store.dispatch(EnvironmentStateAction.AddAllNodes(it.data?.environment?.nodeToPos!!.entries)) + } + } + + hPanel( + FlexWrap.NOWRAP, + JustifyContent.START, + AlignItems.START, + spacing = 5, + ) { + canvas(className = "environment-renderer") { + canvasWidth = DEFAULT_WIDTH + canvasHeight = DEFAULT_HEIGHT + border = Border(width = 1.px, style = BorderStyle.SOLID, color = Color("#ff0000")) + + var cor: Job + + var mouseIsDown = false + + var translatePos: Pair = + Pair(DEFAULT_START_POSITION.toDouble(), DEFAULT_START_POSITION.toDouble()) + + var startDragOffset: Pair = + Pair(DEFAULT_START_POSITION.toDouble(), DEFAULT_START_POSITION.toDouble()) + + addBeforeDisposeHook { + } + + addAfterCreateHook { + + this.bind(scaleTranslationStore) { state -> + context2D.redrawNodes(state.scale, state.translate) + } + + this.bind(nodesRadius) { + context2D.redrawNodes( + scaleTranslationStore.getState().scale, + scaleTranslationStore.getState().translate, + ) + } + + this.bind(EnvironmentStore.store) { + println("Bind to store") + context2D.redrawNodes( + scaleTranslationStore.getState().scale, + scaleTranslationStore.getState().translate, + ) + } + } + + addAfterInsertHook { + } + + onEvent { + mousedown = { e -> + cursor = Cursor.GRABBING + mouseIsDown = true + startDragOffset = + Pair( + e.clientX - translatePos.first, + e.clientY - translatePos.second, + ) + } + mousemove = { e -> + if (mouseIsDown) { + translatePos = + Pair( + e.clientX - startDragOffset.first, + e.clientY - startDragOffset.second, + ) + scaleTranslationStore.dispatch( + ScaleTranslateAction.SetTranslation(translatePos), + ) + } + } + mouseup = { + mouseIsDown = false + cursor = Cursor.DEFAULT + } + mousewheel = { e -> + e.preventDefault() + if (e.deltaY > 0) { + scaleTranslationStore.dispatch( + ScaleTranslateAction.SetScale( + scaleTranslationStore.getState().scale * DEFAULT_SCALE_RATIO, + ), + ) + } else { + scaleTranslationStore.dispatch( + ScaleTranslateAction.SetScale( + scaleTranslationStore.getState().scale / DEFAULT_SCALE_RATIO, + ), + ) + } + } + } + } + + vPanel { + div().bind(scaleTranslationStore) { state -> +"Scale: ${state.scale}" } + + div().bind(scaleTranslationStore) { state -> + +"Translation: ${state.translate.first}, ${state.translate.second}" + } + + range { + label = "Node radius $min - $max" + min = DEFUALT_NODE_RADIUS + max = DEFUALT_NODE_RADIUS * 20 + step = 1.0 + value = DEFUALT_NODE_RADIUS + onInput { + nodesRadius.value = getValue()!!.toDouble() + } + } + + progress(max = scaleTranslationStore.store.getState().scale) { + // this.bounds = (ObservableValue(Bounds(min = 0,))) + }.bind(scaleTranslationStore) { + } + + div().bind(nodesRadius) { + +"Node radius: ${nodesRadius.value}" + } + } + } + } +} diff --git a/alchemist-webgui/src/jsMain/kotlin/components/Navbar.kt b/alchemist-webgui/src/jsMain/kotlin/components/navbar/NavTitle.kt similarity index 89% rename from alchemist-webgui/src/jsMain/kotlin/components/Navbar.kt rename to alchemist-webgui/src/jsMain/kotlin/components/navbar/NavTitle.kt index cd13158069..f387c8c57b 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/Navbar.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/navbar/NavTitle.kt @@ -7,4 +7,6 @@ * as described in the file LICENSE in the Alchemist distribution's top directory. */ -package components +package components.navbar + +class NavTitle diff --git a/alchemist-webgui/src/jsMain/kotlin/components/navbar/PlayButton.kt b/alchemist-webgui/src/jsMain/kotlin/components/navbar/PlayButton.kt new file mode 100644 index 0000000000..2d23fdcfba --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/components/navbar/PlayButton.kt @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package components.navbar + +import graphql.api.SimulationControlApi +import io.kvision.html.Button +import io.kvision.html.ButtonStyle +import io.kvision.html.ButtonType +import io.kvision.state.bind +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.launch +import stores.SimulationStatus +import utils.SimState + +class PlayButton(text: String) : Button(text) { + + private var simulationStatus = SimState.TERMINATED + private var simulationRunning = false + + init { + SimulationStatus.callGetStatus() + + type = ButtonType.BUTTON + labelFirst = false + + this.bind(SimulationStatus.simulationStore) { sim -> + simulationStatus = SimState.toSimStatus(sim.status?.simulationStatus) + simulationRunning = simulationStatus == SimState.RUNNING + + this.text = when (simulationStatus) { + SimState.TERMINATED -> "Terminated" + SimState.RUNNING -> "Pause" + else -> "Play" + } + + icon = when (simulationStatus) { + SimState.TERMINATED -> "fa-solid fa-ban" + SimState.RUNNING -> "fa-solid fa-pause" + else -> "fa-solid fa-play" + } + + style = if (simulationRunning) ButtonStyle.DANGER else ButtonStyle.SUCCESS + disabled = when (simulationStatus) { + SimState.TERMINATED -> true + else -> false + } + } + + onClick { + MainScope().launch { + println("Button clicked coroutine") + + when (simulationStatus) { + SimState.READY -> { + SimulationControlApi.playSimulation() + } + SimState.PAUSED -> { + + SimulationControlApi.playSimulation() + } + SimState.RUNNING -> { + SimulationControlApi.pauseSimulation() + } + else -> { + SimulationControlApi.terminateSimulation() + } + } + SimulationStatus.callGetStatus() + } + } + } +} diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt b/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt new file mode 100644 index 0000000000..5aba89166c --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package stores + +import graphql.api.EnvironmentApi +import io.kvision.redux.createTypedReduxStore +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.launch +import stores.actions.EnvironmentStateAction +import stores.reducers.environmentReducer +import stores.states.EnvironmentState + +object EnvironmentStore { + + val store = createTypedReduxStore(::environmentReducer, EnvironmentState(mutableListOf())) + + fun callEnvironmentSubscription() { + MainScope().launch { + println("Before subscription") + EnvironmentApi.environMentSubScription().collect { response -> + + store.dispatch(EnvironmentStateAction.AddAllNodes(response.data!!.environment.nodeToPos.entries)) + } + println("After subscription") + } + } +} diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/SimulationStatus.kt b/alchemist-webgui/src/jsMain/kotlin/stores/SimulationStatus.kt new file mode 100644 index 0000000000..944f5ee55e --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/stores/SimulationStatus.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package stores + +import graphql.api.SimulationControlApi +import io.kvision.redux.createTypedReduxStore +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.launch +import stores.actions.SimulationAction +import stores.reducers.simulationReducer +import stores.states.SimulationState + +object SimulationStatus { + + val simulationStore = createTypedReduxStore(::simulationReducer, SimulationState()) + + fun callGetStatus() { + MainScope().launch { + println("Call get status coroutine") + val result = SimulationControlApi.getSimulationStatus().await() + + simulationStore.dispatch(SimulationAction.SetSimulation(result)) + } + } +} diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/actions/EnvironmentStateAction.kt b/alchemist-webgui/src/jsMain/kotlin/stores/actions/EnvironmentStateAction.kt index c1f07c3344..fe2e2a5ece 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/actions/EnvironmentStateAction.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/actions/EnvironmentStateAction.kt @@ -9,10 +9,10 @@ package stores.actions -/* import io.kvision.redux.RAction import it.unibo.alchemist.boundary.graphql.client.EnvironmentSubscription sealed class EnvironmentStateAction : RAction { - data class SetNodes(val nodes: EnvironmentSubscription.Data?) : EnvironmentStateAction() -}*/ + data class SetNodes(val nodes: MutableList) : EnvironmentStateAction() + data class AddAllNodes(val nodes: List) : EnvironmentStateAction() +} diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/actions/ScaleTranslateAction.kt b/alchemist-webgui/src/jsMain/kotlin/stores/actions/ScaleTranslateAction.kt index ec9287fa8d..eb2c0ce108 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/actions/ScaleTranslateAction.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/actions/ScaleTranslateAction.kt @@ -9,10 +9,9 @@ package stores.actions -/* import io.kvision.redux.RAction sealed class ScaleTranslateAction : RAction { data class SetScale(val scale: Double) : ScaleTranslateAction() data class SetTranslation(var translate: Pair) : ScaleTranslateAction() -}*/ +} diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/actions/SimulationAction.kt b/alchemist-webgui/src/jsMain/kotlin/stores/actions/SimulationAction.kt new file mode 100644 index 0000000000..5d800401b2 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/stores/actions/SimulationAction.kt @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package stores.actions + +import io.kvision.redux.RAction +import it.unibo.alchemist.boundary.graphql.client.SimulationStatusQuery + +sealed class SimulationAction : RAction { + data class SetSimulation(val simulationState: SimulationStatusQuery.Data?) : SimulationAction() +} diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/reducers/EnvironmentReducer.kt b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/EnvironmentReducer.kt index cceb4a0394..87e5c151ce 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/reducers/EnvironmentReducer.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/EnvironmentReducer.kt @@ -9,12 +9,19 @@ package stores.reducers -/* -import stores.EnvironmentState import stores.actions.EnvironmentStateAction +import stores.states.EnvironmentState + +fun environmentReducer(state: EnvironmentState, action: EnvironmentStateAction): EnvironmentState { + when (action) { + is EnvironmentStateAction.SetNodes -> { + return state.copy(nodes = action.nodes) + } -fun environmentReducer(state: EnvironmentState, action: EnvironmentStateAction): EnvironmentState = when (action) { - is EnvironmentStateAction.SetNodes -> { - state.copy(nodes = action.nodes) + is EnvironmentStateAction.AddAllNodes -> { + state.nodes.clear() + state.nodes.addAll(action.nodes) + return state + } } -}*/ +} diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/reducers/ScaleTranslateReducer.kt b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/ScaleTranslateReducer.kt index 65972315f4..952c2bbedf 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/reducers/ScaleTranslateReducer.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/ScaleTranslateReducer.kt @@ -9,8 +9,8 @@ package stores.reducers -/*import stores.ScaleTranslateState import stores.actions.ScaleTranslateAction +import stores.states.ScaleTranslateState fun scaleTranslateReducer(state: ScaleTranslateState, action: ScaleTranslateAction): ScaleTranslateState = when (action) { is ScaleTranslateAction.SetScale -> { @@ -20,4 +20,4 @@ fun scaleTranslateReducer(state: ScaleTranslateState, action: ScaleTranslateActi is ScaleTranslateAction.SetTranslation -> { state.copy(translate = action.translate) } -}*/ +} diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/reducers/SimulationReducer.kt b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/SimulationReducer.kt new file mode 100644 index 0000000000..72c70b4369 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/SimulationReducer.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package stores.reducers + +import stores.actions.SimulationAction +import stores.states.SimulationState + +fun simulationReducer(state: SimulationState, action: SimulationAction): SimulationState = when (action) { + is SimulationAction.SetSimulation -> { + state.copy(status = action.simulationState) + } +} diff --git a/alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt b/alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt index f961ea4632..8eecc79771 100644 --- a/alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt +++ b/alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt @@ -9,46 +9,16 @@ package ui -/*import components.Content +import components.Appbar +import components.Content import io.kvision.Application import io.kvision.panel.root -import org.w3c.dom.CanvasRenderingContext2D -import org.w3c.dom.HTMLCanvasElement -import web.dom.document -import kotlin.math.PI*/ -/*val connection = ClientConnection(); -private val scope = MainScope() -private const val UPDATE_STATE_DELAY: Long = 100 - -val App = FC { - val (data, setData) = useState(null) - - useEffectOnce { - scope.launch { - val result = connection.retrieveQuery().await() - setData(result) - } - } - div { - h1 { - +"The result of the query:" - } - - h2 { - +NodeQuery.OPERATION_DOCUMENT - } - // Render data from GraphQL query - div { - +"Position is:${data?.nodePosition?.coordinates}" - } - } -}*/ - -/*class App : Application() { +class AppMain : Application() { override fun start() { root("root") { + add(Appbar()) add(Content()) } } -}*/ +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 309827259a..a83a4f81b1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -55,8 +55,8 @@ gradleEnterprise { gitHooks { commitMsg { conventionalCommits() } - preCommit { + /*preCommit { tasks("ktlintCheck", "checkScalafmt", "--parallel") - } + }*/ createHooks(overwriteExisting = true) } From fd91d1c96c71c7439af1db62888a299294d3f84b Mon Sep 17 00:00:00 2001 From: Tiziano Vuksan Date: Mon, 29 Jan 2024 18:08:15 +0100 Subject: [PATCH 03/22] feat(webgui): added nodes subscription rendering nodes now are represented in a canvas context --- .../kotlin/client/ClientConnection.kt | 18 ++++++++++++++++++ .../kotlin/stores/states/EnvironmentState.kt | 14 ++++++++++++++ .../stores/{ => states}/ScaleTranslateState.kt | 10 +++++----- .../SimulationState.kt} | 6 +++--- 4 files changed, 40 insertions(+), 8 deletions(-) create mode 100644 alchemist-webgui/src/commonMain/kotlin/client/ClientConnection.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/stores/states/EnvironmentState.kt rename alchemist-webgui/src/jsMain/kotlin/stores/{ => states}/ScaleTranslateState.kt (63%) rename alchemist-webgui/src/jsMain/kotlin/stores/{EnvironmentState.kt => states/SimulationState.kt} (68%) diff --git a/alchemist-webgui/src/commonMain/kotlin/client/ClientConnection.kt b/alchemist-webgui/src/commonMain/kotlin/client/ClientConnection.kt new file mode 100644 index 0000000000..8e6210a0d9 --- /dev/null +++ b/alchemist-webgui/src/commonMain/kotlin/client/ClientConnection.kt @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package client + +import it.unibo.alchemist.boundary.graphql.client.GraphQLClient +import it.unibo.alchemist.boundary.graphql.client.GraphQLClientFactory + +object ClientConnection { + + val client: GraphQLClient = GraphQLClientFactory.subscriptionClient() +} diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/states/EnvironmentState.kt b/alchemist-webgui/src/jsMain/kotlin/stores/states/EnvironmentState.kt new file mode 100644 index 0000000000..3b9c8f70b7 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/stores/states/EnvironmentState.kt @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package stores.states + +import it.unibo.alchemist.boundary.graphql.client.EnvironmentSubscription + +data class EnvironmentState(val nodes: MutableList) diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/ScaleTranslateState.kt b/alchemist-webgui/src/jsMain/kotlin/stores/states/ScaleTranslateState.kt similarity index 63% rename from alchemist-webgui/src/jsMain/kotlin/stores/ScaleTranslateState.kt rename to alchemist-webgui/src/jsMain/kotlin/stores/states/ScaleTranslateState.kt index 813bb78805..6e743aeb6a 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/ScaleTranslateState.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/states/ScaleTranslateState.kt @@ -7,11 +7,11 @@ * as described in the file LICENSE in the Alchemist distribution's top directory. */ -package stores +package stores.states -/* -import components.Content +import components.content.SimulationContext.Companion.DEFAULT_SCALE data class ScaleTranslateState( - var scale: Double = Content.DEFAULT_SCALE.toDouble(), - var translate: Pair = Pair((Content.DEFAULT_WIDTH /2).toDouble(),(Content.DEFAULT_HEIGHT/2).toDouble()))*/ + var scale: Double = DEFAULT_SCALE.toDouble(), + var translate: Pair = Pair(0.0, 0.0), +) diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentState.kt b/alchemist-webgui/src/jsMain/kotlin/stores/states/SimulationState.kt similarity index 68% rename from alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentState.kt rename to alchemist-webgui/src/jsMain/kotlin/stores/states/SimulationState.kt index 4dad498473..1b1b5aa637 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentState.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/states/SimulationState.kt @@ -7,8 +7,8 @@ * as described in the file LICENSE in the Alchemist distribution's top directory. */ -package stores +package stores.states -/*import it.unibo.alchemist.boundary.graphql.client.EnvironmentSubscription +import it.unibo.alchemist.boundary.graphql.client.SimulationStatusQuery -data class EnvironmentState(val nodes: EnvironmentSubscription.Data? = null)*/ +data class SimulationState(val status: SimulationStatusQuery.Data? = null) From 7f2acf40c4fcf216fe6e42fa21674fa91fe8eeb8 Mon Sep 17 00:00:00 2001 From: Tiziano Vuksan Date: Wed, 31 Jan 2024 22:44:38 +0100 Subject: [PATCH 04/22] feat(webgui): code refactoring restructured web page dom changed style features minor ui tweaks --- alchemist-webgui/build.gradle.kts | 80 ++---- .../kotlin/graphql/api/EnvironmentApi.kt | 7 +- .../graphql/api/SimulationControlApi.kt | 6 +- alchemist-webgui/src/jsMain/kotlin/Client.kt | 10 +- .../src/jsMain/kotlin/components/Appbar.kt | 38 ++- .../src/jsMain/kotlin/components/Content.kt | 24 +- .../components/content/SimulationContext.kt | 239 +++++++----------- .../content/SimulationIndicators.kt | 79 ++++++ .../content/shared/CommonProperties.kt | 45 ++++ .../jsMain/kotlin/stores/EnvironmentStore.kt | 5 +- .../jsMain/kotlin/stores/SimulationStatus.kt | 3 +- .../stores/states/ScaleTranslateState.kt | 2 +- .../src/jsMain/kotlin/ui/AppMain.kt | 12 +- 13 files changed, 298 insertions(+), 252 deletions(-) create mode 100644 alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt diff --git a/alchemist-webgui/build.gradle.kts b/alchemist-webgui/build.gradle.kts index c587c4ce64..ab9f256e56 100644 --- a/alchemist-webgui/build.gradle.kts +++ b/alchemist-webgui/build.gradle.kts @@ -1,5 +1,6 @@ import Libs.alchemist import io.gitlab.arturbosch.detekt.Detekt +import org.jetbrains.kotlin.gradle.targets.js.npm.importedPackageDir import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig group = "it.unibo.alchemist" @@ -22,7 +23,7 @@ repositories { mavenLocal() } -dependencies { +/*dependencies { implementation(alchemist("graphql")) implementation(alchemist("api")) implementation(alchemist("full")) @@ -37,64 +38,44 @@ dependencies { implementation(libs.kotest.runner) implementation(libs.ktor.server.test.host) -} +}*/ -application { +/*application { mainClass.set("it.unibo.alchemist.Alchemist") -} +}*/ val task by tasks.register("runWEB") { + dependencies{ + implementation(alchemist("graphql")) + implementation(alchemist("api")) + implementation(alchemist("full")) + } group = alchemistGroup description = "Launches simulation" - // Custom property to store the simulation file path val simulationFile = project.objects.property(String::class.java) - // Set the property value from the command line or use a default value simulationFile.set(project.findProperty("simulationFile") as String? ?: "C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/Alchemist fork/Alchemist/test.yml") - // effect.set(project.findProperty("effect") as String? ?: "") + mainClass.set("it.unibo.alchemist.Alchemist") - // Add the simulation file path as a program argument args("run", "C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/yaml/" + simulationFile.get()) doFirst { + val libsDir = "C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/Alchemist fork/Alchemist/alchemist-graphql/build/libs/" classpath( sourceSets["main"].runtimeClasspath, - // tasks.named("compileKotlinJvm"), - // configurations.named("jvmRuntimeClasspath"), - files("C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/Alchemist fork/Alchemist/alchemist-graphql/build/libs/alchemist-graphql-0.1.0-archeo+343bdac0b.jar"), - files("C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/Alchemist fork/Alchemist/alchemist-graphql/build/libs/alchemist-graphql-js-0.1.0-archeo+343bdac0b.klib"), - files("C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/Alchemist fork/Alchemist/alchemist-graphql/build/libs/alchemist-graphql-jvm-0.1.0-archeo+343bdac0b.jar"), - files("C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/Alchemist fork/Alchemist/alchemist-graphql/build/libs/alchemist-graphql-kotlin-0.1.0-archeo+343bdac0b-sources.jar"), - files("C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/Alchemist fork/Alchemist/alchemist-graphql/build/libs/alchemist-graphql-metadata-0.1.0-archeo+343bdac0b.jar"), - + fileTree(libsDir) { + include("**/*.jar") + include("**/*.klib") + } ) } // classpath(tasks.named("compileKotlinJvm"), configurations.named("jvmRuntimeClasspath")) - // , "-g","C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/effects/" + effect.get() } -/*tasks { - named("run", JavaExec::class).configure { - classpath(named("compileKotlinJvm"), configurations.named("jvmRuntimeClasspath")) - // val simulationFile = project.objects.property(String::class.java) - // args("run", "C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/yaml/" + simulationFile.get()) - } -} - -tasks.withType().configureEach { - if (name == "sourcesJar") { - dependsOn("kspCommonMainKotlinMetadata") - } -} - -tasks.named("sourcesJar").configure { - dependsOn("kspCommonMainKotlinMetadata") -}*/ - kotlin { jvm { withJava() @@ -123,11 +104,11 @@ kotlin { }, ) - /*testTask(Action { + testTask(Action { useKarma { useChromeHeadless() } - })*/ + }) binaries.executable() } @@ -212,31 +193,6 @@ kotlin { } } - /*val jsTest by getting { - dependencies{ - implementation("io.kvision:kvision-testutils:$kvisionVersion") - } - - }*/ - /*val jsTest by getting { - dependencies { - implementation(alchemist("graphql")) - //implementation(alchemist("api")) - implementation(alchemist("full")) - implementation(libs.apollo.runtime) - implementation(libs.kotlin.coroutines.core) - implementation(libs.kotlin.stdlib) - implementation(libs.kotlinx.serialization.json) - - implementation(libs.ktor.server.websockets) - implementation(libs.bundles.graphql.server) - implementation(libs.bundles.ktor.server) - - implementation(libs.kotest.assertions) - implementation(libs.kotest.runner) - implementation(libs.ktor.server.test.host) - } - }*/ } } diff --git a/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt b/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt index 57c1599a5d..b84eb781a8 100644 --- a/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt +++ b/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt @@ -21,14 +21,15 @@ import kotlinx.coroutines.flow.Flow object EnvironmentApi { - suspend fun retrieveQuery(): Deferred = coroutineScope { + suspend fun retrieveQuery(nodeId: Int = 0): Deferred = coroutineScope { async { - ClientConnection.client.query(NodeQuery(id = Optional.present(10))).execute().data + ClientConnection.client.query(NodeQuery(id = Optional.present(nodeId))).execute().data } } fun environMentSubScription(): Flow> { - // ClientConnection.client.close() + return ClientConnection.client.subscription(EnvironmentSubscription()).toFlow() + } } diff --git a/alchemist-webgui/src/commonMain/kotlin/graphql/api/SimulationControlApi.kt b/alchemist-webgui/src/commonMain/kotlin/graphql/api/SimulationControlApi.kt index 524723eceb..9c767080d0 100644 --- a/alchemist-webgui/src/commonMain/kotlin/graphql/api/SimulationControlApi.kt +++ b/alchemist-webgui/src/commonMain/kotlin/graphql/api/SimulationControlApi.kt @@ -31,8 +31,8 @@ object SimulationControlApi { suspend fun pauseSimulation() = coroutineScope { async { withContext(Dispatchers.Default) { - println("paused the simulation") ClientConnection.client.mutation(PauseSimulationMutation()).execute() + println("COROUTINE[pauseSimulation]: Paused the simulation") } } } @@ -40,8 +40,8 @@ object SimulationControlApi { suspend fun playSimulation() = coroutineScope { async { withContext(Dispatchers.Default) { - println("playing the simulation") ClientConnection.client.mutation(PlaySimulationMutation()).execute() + println("COROUTINE[playSimulation]: Played the simulation") } } } @@ -49,8 +49,8 @@ object SimulationControlApi { suspend fun terminateSimulation() = coroutineScope { async { withContext(Dispatchers.Default) { - println("terminated the simulation") ClientConnection.client.mutation(TerminateSimulationMutation()).execute() + println("COROUTINE[terminateSimulation]: Terminated the simulation") } } } diff --git a/alchemist-webgui/src/jsMain/kotlin/Client.kt b/alchemist-webgui/src/jsMain/kotlin/Client.kt index 7714f0e924..3452e2eeee 100644 --- a/alchemist-webgui/src/jsMain/kotlin/Client.kt +++ b/alchemist-webgui/src/jsMain/kotlin/Client.kt @@ -24,11 +24,11 @@ import io.kvision.ToastifyModule import io.kvision.TomSelectModule import io.kvision.module import io.kvision.startApplication -import ui.App +import ui.AppMain fun main() { startApplication( - ::App, + ::AppMain, module.hot, BootstrapModule, BootstrapCssModule, @@ -47,9 +47,3 @@ fun main() { CoreModule, ) } - -/*fun main() { - val container = document.getElementById("root") ?: error("Couldn't find container!") - createRoot(container.unsafeCast()).render(App.create()) - -}*/ diff --git a/alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt b/alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt index daa271d94b..13e9c0b97c 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt @@ -10,22 +10,33 @@ package components import components.navbar.PlayButton +import io.kvision.core.AlignItems import io.kvision.core.Color +import io.kvision.core.CssSize +import io.kvision.core.FlexDirection +import io.kvision.core.JustifyContent +import io.kvision.core.UNIT +import io.kvision.form.check.checkBox +import io.kvision.form.text.text +import io.kvision.html.InputType +import io.kvision.html.br import io.kvision.html.div +import io.kvision.html.input import io.kvision.navbar.NavbarColor import io.kvision.navbar.NavbarType import io.kvision.navbar.nav +import io.kvision.navbar.navForm import io.kvision.navbar.navbar import io.kvision.panel.SimplePanel +import io.kvision.panel.flexPanel import io.kvision.state.bind import stores.SimulationStatus -open class Appbar() : SimplePanel(className = "appbar-root") { +open class Appbar(className: String = "") : SimplePanel(className = className) { private val simulationFileName = "simulationTest.yml" init { - // SimulationStatus.callGetStatus() navbar( if (simulationFileName.isEmpty()) "Alchemist" else "Alchemist - $simulationFileName", collapseOnClick = false, @@ -33,18 +44,25 @@ open class Appbar() : SimplePanel(className = "appbar-root") { type = NavbarType.STICKYTOP nColor = NavbarColor.DARK - nav { - - div { - color = Color("#ffffff") - }.bind(SimulationStatus.simulationStore) { sim -> - val simStatus = sim.status?.simulationStatus - +"SIMULATION STATUS IS: $simStatus" + navForm { + text(InputType.SEARCH){ + placeholder = "Search node" } } nav(rightAlign = true) { - add(PlayButton("Play")) + flexPanel { + flexDirection = FlexDirection.ROW + alignItems = AlignItems.CENTER + spacing = 15 + div { + color = Color("#ffffff") + }.bind(SimulationStatus.simulationStore) { sim -> + +"STATUS: ${sim.status?.simulationStatus}" + } + + add(PlayButton("Play")) + } } } } diff --git a/alchemist-webgui/src/jsMain/kotlin/components/Content.kt b/alchemist-webgui/src/jsMain/kotlin/components/Content.kt index dc52bac8a0..c197d99d33 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/Content.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/Content.kt @@ -10,6 +10,7 @@ package components import components.content.SimulationContext +import components.content.SimulationIndicators import io.kvision.core.AlignItems import io.kvision.core.FlexDirection import io.kvision.core.FlexWrap @@ -17,16 +18,16 @@ import io.kvision.core.JustifyContent import io.kvision.html.div import io.kvision.panel.SimplePanel import io.kvision.panel.flexPanel +import io.kvision.panel.hPanel import io.kvision.state.bind import stores.EnvironmentStore -open class Content() : SimplePanel(className = "content-root") { +open class Content(className: String = "") : SimplePanel(className = className) { init { - // EnvironmentStore.callEnvironmentSubscription() - // val offcanvas = offcanvas("Lorem ipsum", OffPlacement.END, dark = true) + // offcanvas.show() flexPanel( FlexDirection.ROW, @@ -35,19 +36,24 @@ open class Content() : SimplePanel(className = "content-root") { AlignItems.START, spacing = 5, ) { - div(className = "nodes-list") { + /*div(className = "nodes-list") { bind(EnvironmentStore.store) { env -> env.nodes.forEach { div { +"position: ${it.position.coordinates}" } } } + }*/ + + hPanel( + FlexWrap.NOWRAP, + JustifyContent.START, + AlignItems.START, + spacing = 5, + ) { + add(SimulationContext(className = "simulation-context")) + add(SimulationIndicators(className = "simulation-indicators")) } - - val simulationContext = SimulationContext() - add(simulationContext) } - - // offcanvas.show() } } diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt index 9d851348e0..6f30b5986a 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt @@ -9,54 +9,32 @@ package components.content +import components.content.shared.CommonProperties import graphql.api.EnvironmentApi -import io.kvision.core.AlignItems import io.kvision.core.Border import io.kvision.core.BorderStyle +import io.kvision.core.BoxShadow import io.kvision.core.Color +import io.kvision.core.CssSize import io.kvision.core.Cursor -import io.kvision.core.FlexWrap -import io.kvision.core.JustifyContent +import io.kvision.core.UNIT import io.kvision.core.onEvent -import io.kvision.core.onInput -import io.kvision.form.number.range import io.kvision.html.canvas -import io.kvision.html.div import io.kvision.panel.SimplePanel -import io.kvision.panel.hPanel -import io.kvision.panel.vPanel -import io.kvision.progress.progress -import io.kvision.redux.createTypedReduxStore -import io.kvision.state.ObservableValue import io.kvision.state.bind import io.kvision.utils.px +import korlibs.image.color.Colors import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job import kotlinx.coroutines.launch import org.w3c.dom.CanvasRenderingContext2D import stores.EnvironmentStore import stores.actions.EnvironmentStateAction import stores.actions.ScaleTranslateAction -import stores.reducers.scaleTranslateReducer -import stores.states.ScaleTranslateState import kotlin.math.PI -import kotlin.math.min import kotlin.random.Random -class SimulationContext : SimplePanel(className = "simulation-context") { - - companion object { - const val DEFUALT_NODE_RADIUS = 5.0 - const val DEFAULT_HEIGHT = 1000 - const val DEFAULT_WIDTH = 1000 - const val DEFAULT_SCALE = 25 - const val DEFAULT_START_POSITION = 0 - const val DEFAULT_SCALE_RATIO = 0.8 - const val DEFAULT_NODE_COLOR = "#FF0000" - } - private val scaleTranslationStore = createTypedReduxStore(::scaleTranslateReducer, ScaleTranslateState()) - private var nodesRadius: ObservableValue = ObservableValue(DEFUALT_NODE_RADIUS) +class SimulationContext(className: String = "") : SimplePanel(className = className) { private fun randomColor(): String { val letters = "0123456789ABCDEF" @@ -65,12 +43,16 @@ class SimulationContext : SimplePanel(className = "simulation-context") { return color } - private fun CanvasRenderingContext2D.drawNode(position: Pair, color: String = DEFAULT_NODE_COLOR) { + private fun CanvasRenderingContext2D.drawNode( + position: Pair, + color: String = CommonProperties.RenderProperties.DEFAULT_NODE_COLOR + ) { beginPath() arc( position.first, position.second, - nodesRadius.value * 1 / scaleTranslationStore.getState().scale, + CommonProperties.Observables.nodesRadius.value * 1 / + CommonProperties.Observables.scaleTranslationStore.getState().scale, 0.0, 2 * PI, false, @@ -81,14 +63,15 @@ class SimulationContext : SimplePanel(className = "simulation-context") { } private fun CanvasRenderingContext2D.redrawNodes( - scale: Double = DEFAULT_SCALE.toDouble(), + scale: Double = CommonProperties.RenderProperties.DEFAULT_SCALE.toDouble(), translation: Pair = - Pair(DEFAULT_START_POSITION.toDouble(), DEFAULT_START_POSITION.toDouble()), + Pair(CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble(), + CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble()), ) { - println("Redrawing function") - val translationScaled = Pair( - translation.first * 1 / scaleTranslationStore.getState().scale, - translation.second * 1 / scaleTranslationStore.getState().scale, + println("Function[Redraw]: Redrawing function") + val translationScaled = Pair( + translation.first * 1 / CommonProperties.Observables.scaleTranslationStore.getState().scale, + translation.second * 1 / CommonProperties.Observables.scaleTranslationStore.getState().scale, ) scale(scale, scale) @@ -97,11 +80,18 @@ class SimulationContext : SimplePanel(className = "simulation-context") { clearRect( -translationScaled.first, -translationScaled.second, - DEFAULT_WIDTH.toDouble(), - DEFAULT_HEIGHT.toDouble(), + CommonProperties.RenderProperties.DEFAULT_WIDTH, + CommonProperties.RenderProperties.DEFAULT_HEIGHT, ) - // println("Translated to:"+translationScaled.first+", "+translationScaled.second) + /*beginPath() + strokeStyle = "#000000" + moveTo((CommonProperties.RenderProperties.DEFAULT_WIDTH / 2), 0.0) + lineTo((CommonProperties.RenderProperties.DEFAULT_WIDTH / 2), CommonProperties.RenderProperties.DEFAULT_HEIGHT) + moveTo(0.0, (CommonProperties.RenderProperties.DEFAULT_HEIGHT / 2)) + lineTo(CommonProperties.RenderProperties.DEFAULT_WIDTH, (CommonProperties.RenderProperties.DEFAULT_HEIGHT / 2)) + stroke() + closePath()*/ EnvironmentStore.store.getState().nodes.forEach { drawNode(Pair(it.position.coordinates[0], it.position.coordinates[1]), randomColor()) @@ -111,144 +101,93 @@ class SimulationContext : SimplePanel(className = "simulation-context") { } init { - /*CoroutineScope(Dispatchers.CIO).launch { - println("Init scope update environment store") - EnvironmentApi.environMentSubScription().collect { response -> - //async{ - println("data-->"+response.data) - //}.await() - - //EnvironmentStore.store.dispatch(EnvironmentStateAction.SetNodes(it.data?.environment?.nodeToPos!!.entries.toMutableList())) - } - }*/ CoroutineScope(Dispatchers.Default).launch { + println("COROUTINE[environmentSubscription]: Environmnent subscription started") EnvironmentApi.environMentSubScription().collect { // println("A") EnvironmentStore.store.dispatch(EnvironmentStateAction.AddAllNodes(it.data?.environment?.nodeToPos!!.entries)) } + println("COROUTINE[environmentSubscription]: Environmnent subscription ended") } - hPanel( - FlexWrap.NOWRAP, - JustifyContent.START, - AlignItems.START, - spacing = 5, - ) { - canvas(className = "environment-renderer") { - canvasWidth = DEFAULT_WIDTH - canvasHeight = DEFAULT_HEIGHT - border = Border(width = 1.px, style = BorderStyle.SOLID, color = Color("#ff0000")) + canvas(className = "environment-renderer") { + canvasWidth = CommonProperties.RenderProperties.DEFAULT_WIDTH.toInt() + canvasHeight = CommonProperties.RenderProperties.DEFAULT_HEIGHT.toInt() + borderRadius = CssSize(10, UNIT.px) + border = Border(width = 2.px, BorderStyle.SOLID, Color("#A3A3A3")) - var cor: Job + var mouseIsDown = false - var mouseIsDown = false + var translatePos: Pair = + Pair(CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble(), + CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble()) - var translatePos: Pair = - Pair(DEFAULT_START_POSITION.toDouble(), DEFAULT_START_POSITION.toDouble()) + var startDragOffset: Pair = + Pair(CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble(), + CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble()) - var startDragOffset: Pair = - Pair(DEFAULT_START_POSITION.toDouble(), DEFAULT_START_POSITION.toDouble()) + addAfterCreateHook { - addBeforeDisposeHook { + this.bind(CommonProperties.Observables.scaleTranslationStore) { state -> + context2D.redrawNodes(state.scale, state.translate) } - addAfterCreateHook { + this.bind(CommonProperties.Observables.nodesRadius) { + context2D.redrawNodes( + CommonProperties.Observables.scaleTranslationStore.getState().scale, + CommonProperties.Observables.scaleTranslationStore.getState().translate, + ) + } - this.bind(scaleTranslationStore) { state -> - context2D.redrawNodes(state.scale, state.translate) - } + this.bind(EnvironmentStore.store) { + println("Bind to store") + context2D.redrawNodes( + CommonProperties.Observables.scaleTranslationStore.getState().scale, + CommonProperties.Observables.scaleTranslationStore.getState().translate, + ) + } + } - this.bind(nodesRadius) { - context2D.redrawNodes( - scaleTranslationStore.getState().scale, - scaleTranslationStore.getState().translate, - ) - } + addAfterInsertHook { + } - this.bind(EnvironmentStore.store) { - println("Bind to store") - context2D.redrawNodes( - scaleTranslationStore.getState().scale, - scaleTranslationStore.getState().translate, + onEvent { + mousedown = { e -> + cursor = Cursor.GRABBING + mouseIsDown = true + startDragOffset = + Pair( + e.clientX - translatePos.first, + e.clientY - translatePos.second, ) - } } - - addAfterInsertHook { - } - - onEvent { - mousedown = { e -> - cursor = Cursor.GRABBING - mouseIsDown = true - startDragOffset = + mousemove = { e -> + if (mouseIsDown) { + translatePos = Pair( - e.clientX - translatePos.first, - e.clientY - translatePos.second, + e.clientX - startDragOffset.first, + e.clientY - startDragOffset.second, ) - } - mousemove = { e -> - if (mouseIsDown) { - translatePos = - Pair( - e.clientX - startDragOffset.first, - e.clientY - startDragOffset.second, - ) - scaleTranslationStore.dispatch( - ScaleTranslateAction.SetTranslation(translatePos), - ) - } - } - mouseup = { - mouseIsDown = false - cursor = Cursor.DEFAULT - } - mousewheel = { e -> - e.preventDefault() - if (e.deltaY > 0) { - scaleTranslationStore.dispatch( - ScaleTranslateAction.SetScale( - scaleTranslationStore.getState().scale * DEFAULT_SCALE_RATIO, - ), - ) - } else { - scaleTranslationStore.dispatch( - ScaleTranslateAction.SetScale( - scaleTranslationStore.getState().scale / DEFAULT_SCALE_RATIO, - ), - ) - } + CommonProperties.Observables.scaleTranslationStore.dispatch( + ScaleTranslateAction.SetTranslation(translatePos), + ) } } - } - - vPanel { - div().bind(scaleTranslationStore) { state -> +"Scale: ${state.scale}" } - - div().bind(scaleTranslationStore) { state -> - +"Translation: ${state.translate.first}, ${state.translate.second}" + mouseup = { + mouseIsDown = false + cursor = Cursor.DEFAULT } - - range { - label = "Node radius $min - $max" - min = DEFUALT_NODE_RADIUS - max = DEFUALT_NODE_RADIUS * 20 - step = 1.0 - value = DEFUALT_NODE_RADIUS - onInput { - nodesRadius.value = getValue()!!.toDouble() + mousewheel = { e -> + e.preventDefault() + val nextScale = CommonProperties.Utils.nextScale(e.deltaY) + + if(nextScale <= CommonProperties.RenderProperties.MAX_SCALE && + nextScale >= CommonProperties.RenderProperties.MIN_SCALE) { + CommonProperties.Observables.scaleTranslationStore + .dispatch(ScaleTranslateAction.SetScale(nextScale)) } } - - progress(max = scaleTranslationStore.store.getState().scale) { - // this.bounds = (ObservableValue(Bounds(min = 0,))) - }.bind(scaleTranslationStore) { - } - - div().bind(nodesRadius) { - +"Node radius: ${nodesRadius.value}" - } } } } diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt new file mode 100644 index 0000000000..e2620796e2 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package components.content + +import components.content.shared.CommonProperties +import io.kvision.core.AlignItems +import io.kvision.core.Border +import io.kvision.core.BorderStyle +import io.kvision.core.Color +import io.kvision.core.UNIT +import io.kvision.core.onInput +import io.kvision.form.number.range +import io.kvision.html.div +import io.kvision.html.p +import io.kvision.panel.SimplePanel +import io.kvision.panel.vPanel +import io.kvision.progress.progress +import io.kvision.progress.progressNumeric +import io.kvision.state.bind +import io.kvision.utils.px +import korlibs.math.roundDecimalPlaces +import kotlin.math.roundToInt + +class SimulationIndicators(className: String = "") : SimplePanel(className = className) { + + init { + vPanel { + spacing = 5 + borderRadius = 10.px + border = Border(width = 2.px, BorderStyle.SOLID, Color("#A3A3A3")) + + + div().bind(CommonProperties.Observables.scaleTranslationStore) { state -> + +"Scale: ${state.scale}" + } + + div().bind(CommonProperties.Observables.scaleTranslationStore) { state -> + +"Translation: ${state.translate.first}, ${state.translate.second}" + } + + range { + label = "Node radius $min - $max" + min = CommonProperties.RenderProperties.DEFUALT_NODE_RADIUS + max = CommonProperties.RenderProperties.DEFUALT_NODE_RADIUS * 20 + step = 1.0 + value = CommonProperties.RenderProperties.DEFUALT_NODE_RADIUS + onInput { + CommonProperties.Observables.nodesRadius.value = getValue()!!.toDouble() + } + } + + div().bind(CommonProperties.Observables.nodesRadius) { + +"Node radius: ${CommonProperties.Observables.nodesRadius.value}" + } + + p(className = "scale-value").bind(CommonProperties.Observables.scaleTranslationStore){ state -> + +"Zoom: ${((state.scale/CommonProperties.RenderProperties.MAX_SCALE) * 100).roundDecimalPlaces(0)}%" + } + + progress( + min = CommonProperties.RenderProperties.MIN_SCALE, + max = CommonProperties.RenderProperties.MAX_SCALE + ) { + progressNumeric { + striped = false + } + }.getFirstProgressBar()!!.bind(CommonProperties.Observables.scaleTranslationStore) { state -> + value = state.scale + } + } + } +} \ No newline at end of file diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt new file mode 100644 index 0000000000..ab1d0f3253 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package components.content.shared + +import components.content.SimulationContext +import io.kvision.redux.createTypedReduxStore +import io.kvision.state.ObservableValue +import stores.reducers.scaleTranslateReducer +import stores.states.ScaleTranslateState + +class CommonProperties { + + object RenderProperties { + const val DEFUALT_NODE_RADIUS = 5.0 + const val DEFAULT_HEIGHT = 900.0 + const val DEFAULT_WIDTH = 1400.0 + const val DEFAULT_SCALE = 25 + const val DEFAULT_START_POSITION = 0 + const val DEFAULT_SCALE_RATIO = 0.8 + const val DEFAULT_NODE_COLOR = "#FF0000" + const val MIN_SCALE = 1.0 + const val MAX_SCALE = 1111.0 + } + + object Observables{ + val scaleTranslationStore = createTypedReduxStore(::scaleTranslateReducer, ScaleTranslateState()) + var nodesRadius: ObservableValue = ObservableValue(RenderProperties.DEFUALT_NODE_RADIUS) + } + + object Utils{ + fun nextScale(threshold: Double): Double{ + return if(threshold > 0) + Observables.scaleTranslationStore.getState().scale * RenderProperties.DEFAULT_SCALE_RATIO + else + Observables.scaleTranslationStore.getState().scale / RenderProperties.DEFAULT_SCALE_RATIO + } + } +} \ No newline at end of file diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt b/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt index 5aba89166c..370897c42b 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt @@ -23,12 +23,13 @@ object EnvironmentStore { fun callEnvironmentSubscription() { MainScope().launch { - println("Before subscription") + println("COROUTINE[callEnvironmentSubscription]: Started") EnvironmentApi.environMentSubScription().collect { response -> store.dispatch(EnvironmentStateAction.AddAllNodes(response.data!!.environment.nodeToPos.entries)) + } - println("After subscription") + println("COROUTINE[callEnvironmentSubscription]: Ended") } } } diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/SimulationStatus.kt b/alchemist-webgui/src/jsMain/kotlin/stores/SimulationStatus.kt index 944f5ee55e..c696767c89 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/SimulationStatus.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/SimulationStatus.kt @@ -23,10 +23,11 @@ object SimulationStatus { fun callGetStatus() { MainScope().launch { - println("Call get status coroutine") + println("COROUTINE[CALLGETSTATUS]: Started") val result = SimulationControlApi.getSimulationStatus().await() simulationStore.dispatch(SimulationAction.SetSimulation(result)) + println("COROUTINE[CALLGETSTATUS]: Ended") } } } diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/states/ScaleTranslateState.kt b/alchemist-webgui/src/jsMain/kotlin/stores/states/ScaleTranslateState.kt index 6e743aeb6a..03bc95abc1 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/states/ScaleTranslateState.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/states/ScaleTranslateState.kt @@ -9,7 +9,7 @@ package stores.states -import components.content.SimulationContext.Companion.DEFAULT_SCALE +import components.content.shared.CommonProperties.RenderProperties.DEFAULT_SCALE data class ScaleTranslateState( var scale: Double = DEFAULT_SCALE.toDouble(), diff --git a/alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt b/alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt index 8eecc79771..ba6fff0379 100644 --- a/alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt +++ b/alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt @@ -12,13 +12,19 @@ package ui import components.Appbar import components.Content import io.kvision.Application +import io.kvision.core.AlignItems +import io.kvision.core.CssSize +import io.kvision.core.FlexDirection +import io.kvision.core.JustifyContent +import io.kvision.core.UNIT +import io.kvision.panel.flexPanel import io.kvision.panel.root class AppMain : Application() { override fun start() { - root("root") { - add(Appbar()) - add(Content()) + root(id="root") { + add(Appbar(className = "appbar-root")) + add(Content(className = "content-root")) } } } From 2a3f0f4828f1ecd14060e68f80de49e056945e96 Mon Sep 17 00:00:00 2001 From: Tiziano Vuksan Date: Thu, 1 Feb 2024 19:11:52 +0100 Subject: [PATCH 05/22] feat(webgui): Added grid mesh visualization fix(webgui): Fixed canvas zooming bug --- alchemist-webgui/build.gradle.kts | 19 +- .../kotlin/graphql/api/EnvironmentApi.kt | 2 - .../src/jsMain/kotlin/components/Appbar.kt | 8 +- .../src/jsMain/kotlin/components/Content.kt | 51 +++- .../components/content/SimulationContext.kt | 226 ++++++++++++------ .../content/SimulationIndicators.kt | 19 +- .../content/shared/CommonProperties.kt | 25 +- .../jsMain/kotlin/stores/EnvironmentStore.kt | 1 - .../src/jsMain/kotlin/ui/AppMain.kt | 8 +- 9 files changed, 226 insertions(+), 133 deletions(-) diff --git a/alchemist-webgui/build.gradle.kts b/alchemist-webgui/build.gradle.kts index ab9f256e56..a6090aa02e 100644 --- a/alchemist-webgui/build.gradle.kts +++ b/alchemist-webgui/build.gradle.kts @@ -1,6 +1,5 @@ import Libs.alchemist import io.gitlab.arturbosch.detekt.Detekt -import org.jetbrains.kotlin.gradle.targets.js.npm.importedPackageDir import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig group = "it.unibo.alchemist" @@ -45,7 +44,7 @@ repositories { }*/ val task by tasks.register("runWEB") { - dependencies{ + dependencies { implementation(alchemist("graphql")) implementation(alchemist("api")) implementation(alchemist("full")) @@ -68,12 +67,11 @@ val task by tasks.register("runWEB") { fileTree(libsDir) { include("**/*.jar") include("**/*.klib") - } + }, ) } // classpath(tasks.named("compileKotlinJvm"), configurations.named("jvmRuntimeClasspath")) - } kotlin { @@ -104,11 +102,13 @@ kotlin { }, ) - testTask(Action { - useKarma { - useChromeHeadless() - } - }) + testTask( + Action { + useKarma { + useChromeHeadless() + } + }, + ) binaries.executable() } @@ -192,7 +192,6 @@ kotlin { implementation("io.kvision:kvision-redux-kotlin:$kvisionVersion") } } - } } diff --git a/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt b/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt index b84eb781a8..dee336f847 100644 --- a/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt +++ b/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt @@ -28,8 +28,6 @@ object EnvironmentApi { } fun environMentSubScription(): Flow> { - return ClientConnection.client.subscription(EnvironmentSubscription()).toFlow() - } } diff --git a/alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt b/alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt index 13e9c0b97c..0a44b68040 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt @@ -12,16 +12,10 @@ package components import components.navbar.PlayButton import io.kvision.core.AlignItems import io.kvision.core.Color -import io.kvision.core.CssSize import io.kvision.core.FlexDirection -import io.kvision.core.JustifyContent -import io.kvision.core.UNIT -import io.kvision.form.check.checkBox import io.kvision.form.text.text import io.kvision.html.InputType -import io.kvision.html.br import io.kvision.html.div -import io.kvision.html.input import io.kvision.navbar.NavbarColor import io.kvision.navbar.NavbarType import io.kvision.navbar.nav @@ -45,7 +39,7 @@ open class Appbar(className: String = "") : SimplePanel(className = className) { nColor = NavbarColor.DARK navForm { - text(InputType.SEARCH){ + text(InputType.SEARCH) { placeholder = "Search node" } } diff --git a/alchemist-webgui/src/jsMain/kotlin/components/Content.kt b/alchemist-webgui/src/jsMain/kotlin/components/Content.kt index c197d99d33..4e763795a0 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/Content.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/Content.kt @@ -12,15 +12,22 @@ package components import components.content.SimulationContext import components.content.SimulationIndicators import io.kvision.core.AlignItems +import io.kvision.core.Background +import io.kvision.core.BoxShadow +import io.kvision.core.Col +import io.kvision.core.Color +import io.kvision.core.CssSize import io.kvision.core.FlexDirection import io.kvision.core.FlexWrap import io.kvision.core.JustifyContent +import io.kvision.core.UNIT import io.kvision.html.div import io.kvision.panel.SimplePanel import io.kvision.panel.flexPanel import io.kvision.panel.hPanel -import io.kvision.state.bind -import stores.EnvironmentStore +import io.kvision.panel.vPanel +import io.kvision.utils.perc +import io.kvision.utils.px open class Content(className: String = "") : SimplePanel(className = className) { @@ -29,13 +36,13 @@ open class Content(className: String = "") : SimplePanel(className = className) // val offcanvas = offcanvas("Lorem ipsum", OffPlacement.END, dark = true) // offcanvas.show() - flexPanel( + /*flexPanel( FlexDirection.ROW, FlexWrap.WRAP, JustifyContent.CENTER, AlignItems.START, spacing = 5, - ) { + ) {*/ /*div(className = "nodes-list") { bind(EnvironmentStore.store) { env -> @@ -47,13 +54,41 @@ open class Content(className: String = "") : SimplePanel(className = className) hPanel( FlexWrap.NOWRAP, - JustifyContent.START, + JustifyContent.CENTER, AlignItems.START, spacing = 5, ) { - add(SimulationContext(className = "simulation-context")) - add(SimulationIndicators(className = "simulation-indicators")) + background = Background(color = Color.name(Col.LIGHTGRAY)) + + add( + SimulationContext(className = "simulation-context").apply{ + width = 1400.px + height = 900.px + } + ) + add( + vPanel( + JustifyContent.CENTER, + AlignItems.START, + spacing = 5, + ) { + width = 520.px + add( + SimulationIndicators(className = "simulation-indicators").apply{ + width = 520.px + } + ) + div{ + flexGrow = 1 + height = 100.perc + borderRadius = CssSize(10, UNIT.px) + boxShadow = BoxShadow(0.px, 0.px, 5.px, 0.px, Color.rgba(0, 0, 0, (0.5 * 255).toInt())) + background = Background(color = Color.name(Col.WHITE)) + } + + } + ) } - } + //} } } diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt index 6f30b5986a..65c8c156b2 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt @@ -11,31 +11,48 @@ package components.content import components.content.shared.CommonProperties import graphql.api.EnvironmentApi +import io.kvision.core.AlignContent +import io.kvision.core.AlignItems +import io.kvision.core.Background import io.kvision.core.Border import io.kvision.core.BorderStyle import io.kvision.core.BoxShadow +import io.kvision.core.Col import io.kvision.core.Color import io.kvision.core.CssSize import io.kvision.core.Cursor +import io.kvision.core.FlexDirection +import io.kvision.core.FlexWrap +import io.kvision.core.JustifyContent import io.kvision.core.UNIT import io.kvision.core.onEvent import io.kvision.html.canvas import io.kvision.panel.SimplePanel +import io.kvision.panel.flexPanel import io.kvision.state.bind +import io.kvision.utils.perc import io.kvision.utils.px -import korlibs.image.color.Colors import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.w3c.dom.CanvasRenderingContext2D +import org.w3c.dom.DOMRect import stores.EnvironmentStore import stores.actions.EnvironmentStateAction import stores.actions.ScaleTranslateAction import kotlin.math.PI +import kotlin.math.ceil import kotlin.random.Random class SimulationContext(className: String = "") : SimplePanel(className = className) { + + data class Node(val id: String, val x: Double, val y: Double, val color: String) + + private val nodes = List(50) { index -> + Node("${index + 1}", Random.nextDouble(0.0, 400.0), Random.nextDouble(0.0, 200.0), randomColor()) + } + private fun randomColor(): String { val letters = "0123456789ABCDEF" var color = "#" @@ -45,7 +62,7 @@ class SimulationContext(className: String = "") : SimplePanel(className = classN private fun CanvasRenderingContext2D.drawNode( position: Pair, - color: String = CommonProperties.RenderProperties.DEFAULT_NODE_COLOR + color: String = CommonProperties.RenderProperties.DEFAULT_NODE_COLOR, ) { beginPath() arc( @@ -63,15 +80,13 @@ class SimulationContext(className: String = "") : SimplePanel(className = classN } private fun CanvasRenderingContext2D.redrawNodes( - scale: Double = CommonProperties.RenderProperties.DEFAULT_SCALE.toDouble(), - translation: Pair = - Pair(CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble(), - CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble()), + scale: Double = CommonProperties.Observables.scaleTranslationStore.getState().scale, + translation: Pair = CommonProperties.Observables.scaleTranslationStore.getState().translate, ) { println("Function[Redraw]: Redrawing function") val translationScaled = Pair( - translation.first * 1 / CommonProperties.Observables.scaleTranslationStore.getState().scale, - translation.second * 1 / CommonProperties.Observables.scaleTranslationStore.getState().scale, + translation.first * 1 / scale, + translation.second * 1 / scale, ) scale(scale, scale) @@ -84,24 +99,46 @@ class SimulationContext(className: String = "") : SimplePanel(className = classN CommonProperties.RenderProperties.DEFAULT_HEIGHT, ) - /*beginPath() - strokeStyle = "#000000" - moveTo((CommonProperties.RenderProperties.DEFAULT_WIDTH / 2), 0.0) - lineTo((CommonProperties.RenderProperties.DEFAULT_WIDTH / 2), CommonProperties.RenderProperties.DEFAULT_HEIGHT) - moveTo(0.0, (CommonProperties.RenderProperties.DEFAULT_HEIGHT / 2)) - lineTo(CommonProperties.RenderProperties.DEFAULT_WIDTH, (CommonProperties.RenderProperties.DEFAULT_HEIGHT / 2)) - stroke() - closePath()*/ + drawGrid() - EnvironmentStore.store.getState().nodes.forEach { + /*EnvironmentStore.store.getState().nodes.forEach { drawNode(Pair(it.position.coordinates[0], it.position.coordinates[1]), randomColor()) + }*/ + + nodes.forEach { + drawNode(Pair(it.x, it.y), it.color) } setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) } + private fun CanvasRenderingContext2D.drawGrid(){ + strokeStyle = "#ccc" + lineWidth = 1.0 / CommonProperties.Observables.scaleTranslationStore.getState().scale + + val stepP = 10 + val left = 0.5 - ceil(CommonProperties.RenderProperties.DEFAULT_WIDTH / stepP) * stepP + val top = 0.5 - ceil(CommonProperties.RenderProperties.DEFAULT_HEIGHT / stepP) * stepP + val right = 2 * CommonProperties.RenderProperties.DEFAULT_WIDTH + val bottom = 2 * CommonProperties.RenderProperties.DEFAULT_HEIGHT + clearRect(left, top, right - left, bottom - top) + beginPath() + for (x in left.toInt() until right.toInt() + stepP step stepP) { + moveTo(x.toDouble(), top) + lineTo(x.toDouble(), bottom) + } + for (y in top.toInt() until bottom.toInt() + stepP step stepP) { + moveTo(left, y.toDouble()) + lineTo(right, y.toDouble()) + } + strokeStyle = "#ADADAD" + stroke() + } + init { + border = Border(width = 1.px, BorderStyle.SOLID, Color("#ff0000")) + CoroutineScope(Dispatchers.Default).launch { println("COROUTINE[environmentSubscription]: Environmnent subscription started") EnvironmentApi.environMentSubScription().collect { @@ -111,81 +148,112 @@ class SimulationContext(className: String = "") : SimplePanel(className = classN println("COROUTINE[environmentSubscription]: Environmnent subscription ended") } - canvas(className = "environment-renderer") { - canvasWidth = CommonProperties.RenderProperties.DEFAULT_WIDTH.toInt() - canvasHeight = CommonProperties.RenderProperties.DEFAULT_HEIGHT.toInt() - borderRadius = CssSize(10, UNIT.px) - border = Border(width = 2.px, BorderStyle.SOLID, Color("#A3A3A3")) + flexPanel( + FlexDirection.COLUMN, + FlexWrap.NOWRAP, + JustifyContent.CENTER, + AlignItems.CENTER, + AlignContent.CENTER + ) { + height = 100.perc - var mouseIsDown = false + canvas(className = "environment-renderer") { + canvasWidth = CommonProperties.RenderProperties.DEFAULT_WIDTH.toInt() + canvasHeight = CommonProperties.RenderProperties.DEFAULT_HEIGHT.toInt() + borderRadius = CssSize(10, UNIT.px) + boxShadow = BoxShadow(0.px, 0.px, 5.px, 0.px, Color.rgba(0, 0, 0, (0.5 * 255).toInt())) + background = Background(color = Color.name(Col.WHITE)) - var translatePos: Pair = - Pair(CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble(), - CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble()) + var mouseIsDown = false - var startDragOffset: Pair = - Pair(CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble(), - CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble()) + var translatePos: Pair - addAfterCreateHook { + var startDragOffset: Pair = + Pair( + CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble(), + CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble(), + ) + lateinit var boundingRect: DOMRect - this.bind(CommonProperties.Observables.scaleTranslationStore) { state -> - context2D.redrawNodes(state.scale, state.translate) - } - this.bind(CommonProperties.Observables.nodesRadius) { - context2D.redrawNodes( - CommonProperties.Observables.scaleTranslationStore.getState().scale, - CommonProperties.Observables.scaleTranslationStore.getState().translate, - ) - } + addAfterCreateHook { - this.bind(EnvironmentStore.store) { - println("Bind to store") - context2D.redrawNodes( - CommonProperties.Observables.scaleTranslationStore.getState().scale, - CommonProperties.Observables.scaleTranslationStore.getState().translate, - ) - } - } + boundingRect = getElement()!!.getBoundingClientRect() - addAfterInsertHook { - } + bind(CommonProperties.Observables.scaleTranslationStore) { state -> + context2D.redrawNodes(state.scale, state.translate) + } - onEvent { - mousedown = { e -> - cursor = Cursor.GRABBING - mouseIsDown = true - startDragOffset = - Pair( - e.clientX - translatePos.first, - e.clientY - translatePos.second, - ) + bind(CommonProperties.Observables.nodesRadius) { + context2D.redrawNodes() + } + + bind(EnvironmentStore.store) { + println("Bind[EnvironmentStore.store]: Redrawing nodes on store bind") + context2D.redrawNodes() + } } - mousemove = { e -> - if (mouseIsDown) { - translatePos = - Pair( + + onEvent { + mousedown = { e -> + cursor = Cursor.GRABBING + mouseIsDown = true + startDragOffset = Pair( + e.clientX - CommonProperties.Observables.scaleTranslationStore.getState().translate.first, + e.clientY - CommonProperties.Observables.scaleTranslationStore.getState().translate.second + ) + } + + mousemove = { e -> + if (mouseIsDown) { + + translatePos = Pair( e.clientX - startDragOffset.first, - e.clientY - startDragOffset.second, + e.clientY - startDragOffset.second ) - CommonProperties.Observables.scaleTranslationStore.dispatch( - ScaleTranslateAction.SetTranslation(translatePos), - ) + + CommonProperties.Observables.scaleTranslationStore.dispatch( + ScaleTranslateAction.SetTranslation(translatePos) + ) + } } - } - mouseup = { - mouseIsDown = false - cursor = Cursor.DEFAULT - } - mousewheel = { e -> - e.preventDefault() - val nextScale = CommonProperties.Utils.nextScale(e.deltaY) - - if(nextScale <= CommonProperties.RenderProperties.MAX_SCALE && - nextScale >= CommonProperties.RenderProperties.MIN_SCALE) { - CommonProperties.Observables.scaleTranslationStore - .dispatch(ScaleTranslateAction.SetScale(nextScale)) + + mouseup = { + mouseIsDown = false + cursor = Cursor.DEFAULT + } + + mousewheel = { e -> + e.preventDefault() + val nextScale = CommonProperties.Utils.nextScale(e.deltaY) + + if (nextScale <= CommonProperties.RenderProperties.MAX_SCALE && + nextScale >= CommonProperties.RenderProperties.MIN_SCALE + ) { + val currentState = CommonProperties.Observables.scaleTranslationStore.getState() + + val scaleChangeFactor = nextScale / currentState.scale + val translationChangeX = (1 - scaleChangeFactor) * (e.clientX - boundingRect.left - currentState.translate.first) + val translationChangeY = (1 - scaleChangeFactor) * (e.clientY - boundingRect.top - currentState.translate.second) + + translatePos = Pair( + translationChangeX, + translationChangeY + ) + + CommonProperties.Observables.scaleTranslationStore.dispatch( + ScaleTranslateAction.SetScale(nextScale), + + ) + CommonProperties.Observables.scaleTranslationStore.dispatch( + ScaleTranslateAction.SetTranslation( + Pair( + currentState.translate.first + translationChangeX, + currentState.translate.second + translationChangeY + ) + ) + ) + } } } } diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt index e2620796e2..365f51f4b4 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt @@ -10,11 +10,11 @@ package components.content import components.content.shared.CommonProperties -import io.kvision.core.AlignItems +import io.kvision.core.Background import io.kvision.core.Border import io.kvision.core.BorderStyle +import io.kvision.core.Col import io.kvision.core.Color -import io.kvision.core.UNIT import io.kvision.core.onInput import io.kvision.form.number.range import io.kvision.html.div @@ -24,19 +24,24 @@ import io.kvision.panel.vPanel import io.kvision.progress.progress import io.kvision.progress.progressNumeric import io.kvision.state.bind +import io.kvision.utils.perc import io.kvision.utils.px import korlibs.math.roundDecimalPlaces -import kotlin.math.roundToInt class SimulationIndicators(className: String = "") : SimplePanel(className = className) { init { + border = Border(width = 1.px, BorderStyle.SOLID, Color("#ff0000")) + height = 100.perc + vPanel { + height = 95.perc + width = 95.perc spacing = 5 borderRadius = 10.px + background = Background(color = Color.name(Col.WHITE)) border = Border(width = 2.px, BorderStyle.SOLID, Color("#A3A3A3")) - div().bind(CommonProperties.Observables.scaleTranslationStore) { state -> +"Scale: ${state.scale}" } @@ -60,13 +65,13 @@ class SimulationIndicators(className: String = "") : SimplePanel(className = cla +"Node radius: ${CommonProperties.Observables.nodesRadius.value}" } - p(className = "scale-value").bind(CommonProperties.Observables.scaleTranslationStore){ state -> + p(className = "scale-value").bind(CommonProperties.Observables.scaleTranslationStore) { state -> +"Zoom: ${((state.scale/CommonProperties.RenderProperties.MAX_SCALE) * 100).roundDecimalPlaces(0)}%" } progress( min = CommonProperties.RenderProperties.MIN_SCALE, - max = CommonProperties.RenderProperties.MAX_SCALE + max = CommonProperties.RenderProperties.MAX_SCALE, ) { progressNumeric { striped = false @@ -76,4 +81,4 @@ class SimulationIndicators(className: String = "") : SimplePanel(className = cla } } } -} \ No newline at end of file +} diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt index ab1d0f3253..6554709b72 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt @@ -9,7 +9,6 @@ package components.content.shared -import components.content.SimulationContext import io.kvision.redux.createTypedReduxStore import io.kvision.state.ObservableValue import stores.reducers.scaleTranslateReducer @@ -18,28 +17,30 @@ import stores.states.ScaleTranslateState class CommonProperties { object RenderProperties { - const val DEFUALT_NODE_RADIUS = 5.0 - const val DEFAULT_HEIGHT = 900.0 - const val DEFAULT_WIDTH = 1400.0 - const val DEFAULT_SCALE = 25 + private const val SCALE_ITERATIONS = 25 + const val DEFUALT_NODE_RADIUS = 10.0 + const val DEFAULT_HEIGHT = 875.0 + const val DEFAULT_WIDTH = 1375.0 + const val DEFAULT_SCALE = 1.0 const val DEFAULT_START_POSITION = 0 const val DEFAULT_SCALE_RATIO = 0.8 const val DEFAULT_NODE_COLOR = "#FF0000" const val MIN_SCALE = 1.0 - const val MAX_SCALE = 1111.0 + const val MAX_SCALE = MIN_SCALE * (1/DEFAULT_SCALE_RATIO) * SCALE_ITERATIONS } - object Observables{ + object Observables { val scaleTranslationStore = createTypedReduxStore(::scaleTranslateReducer, ScaleTranslateState()) var nodesRadius: ObservableValue = ObservableValue(RenderProperties.DEFUALT_NODE_RADIUS) } - object Utils{ - fun nextScale(threshold: Double): Double{ - return if(threshold > 0) + object Utils { + fun nextScale(threshold: Double): Double { + return if (threshold > 0) { Observables.scaleTranslationStore.getState().scale * RenderProperties.DEFAULT_SCALE_RATIO - else + } else { Observables.scaleTranslationStore.getState().scale / RenderProperties.DEFAULT_SCALE_RATIO + } } } -} \ No newline at end of file +} diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt b/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt index 370897c42b..55ce9b2aff 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt @@ -27,7 +27,6 @@ object EnvironmentStore { EnvironmentApi.environMentSubScription().collect { response -> store.dispatch(EnvironmentStateAction.AddAllNodes(response.data!!.environment.nodeToPos.entries)) - } println("COROUTINE[callEnvironmentSubscription]: Ended") } diff --git a/alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt b/alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt index ba6fff0379..8d144ba41c 100644 --- a/alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt +++ b/alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt @@ -12,17 +12,11 @@ package ui import components.Appbar import components.Content import io.kvision.Application -import io.kvision.core.AlignItems -import io.kvision.core.CssSize -import io.kvision.core.FlexDirection -import io.kvision.core.JustifyContent -import io.kvision.core.UNIT -import io.kvision.panel.flexPanel import io.kvision.panel.root class AppMain : Application() { override fun start() { - root(id="root") { + root(id = "root") { add(Appbar(className = "appbar-root")) add(Content(className = "content-root")) } From e3e4a8c52369466a05ab7f1d595bda2a46fd8087 Mon Sep 17 00:00:00 2001 From: AngeloFilaseta Date: Thu, 1 Feb 2024 14:31:04 +0100 Subject: [PATCH 06/22] fix: import of common components from mp projects --- alchemist-graphql/build.gradle.kts | 18 ------------------ alchemist-web-renderer/build.gradle.kts | 17 ----------------- 2 files changed, 35 deletions(-) diff --git a/alchemist-graphql/build.gradle.kts b/alchemist-graphql/build.gradle.kts index e845143975..3d1a60e38e 100644 --- a/alchemist-graphql/build.gradle.kts +++ b/alchemist-graphql/build.gradle.kts @@ -16,10 +16,8 @@ import io.gitlab.arturbosch.detekt.Detekt import java.io.File.separator plugins { - application alias(libs.plugins.kotest.multiplatform) alias(libs.plugins.kotlin.serialization) - alias(libs.plugins.ktor) alias(libs.plugins.graphql.server) alias(libs.plugins.graphql.client) } @@ -70,10 +68,6 @@ kotlin { } } -application { - mainClass.set("it.unibo.alchemist.Alchemist") -} - graphql { schema { packages = listOf( @@ -127,18 +121,6 @@ tasks.withType().configureEach { */ val webpackTask = tasks.named("jsBrowserProductionWebpack") -tasks.named("run", JavaExec::class).configure { - classpath( - tasks.named("compileKotlinJvm"), - configurations.named("jvmRuntimeClasspath"), - webpackTask.map { task -> - task.outputs.files.map { file -> - file.parent - } - }, - ) -} - /** * Configure the [ShadowJar] task to work exactly like the "jvmJar" task of Kotlin Multiplatform, but also * include the JS artifacts by depending on the "jsBrowserProductionWebpack" task. diff --git a/alchemist-web-renderer/build.gradle.kts b/alchemist-web-renderer/build.gradle.kts index 498e788e96..d7f2fa134f 100644 --- a/alchemist-web-renderer/build.gradle.kts +++ b/alchemist-web-renderer/build.gradle.kts @@ -12,7 +12,6 @@ import Libs.incarnation import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar plugins { - application alias(libs.plugins.kotlin.serialization) alias(libs.plugins.kotest.multiplatform) } @@ -87,27 +86,11 @@ kotlin { } } -application { - mainClass.set("it.unibo.alchemist.Alchemist") -} - /** * Webpack task that generates the JS artifacts. */ val webpackTask = tasks.named("jsBrowserProductionWebpack") -tasks.named("run", JavaExec::class).configure { - classpath( - tasks.named("compileKotlinJvm"), - configurations.named("jvmRuntimeClasspath"), - webpackTask.map { task -> - task.outputs.files.map { file -> - file.parent - } - }, - ) -} - /** * Configure the [ShadowJar] task to work exactly like the "jvmJar" task of Kotlin Multiplatform, but also * include the JS artifacts by depending on the "jsBrowserProductionWebpack" task. From 40d4eb0f1c8008d043e3869a209a4435433d1dfb Mon Sep 17 00:00:00 2001 From: AngeloFilaseta Date: Thu, 1 Feb 2024 15:49:28 +0100 Subject: [PATCH 07/22] build: move javaQA and multiJvmTesting to jvm only projects --- build.gradle.kts | 132 +++++++++++++++++++++++------------------------ 1 file changed, 65 insertions(+), 67 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 0e6dc715b8..d4c11fffd6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -44,17 +44,16 @@ plugins { val minJavaVersion: String by properties allprojects { - with(rootProject.libs.plugins) { if (project.isMultiplatform) { apply(plugin = kotlin.multiplatform.id) } else { apply(plugin = kotlin.jvm.id) + apply(plugin = multiJvmTesting.id) + apply(plugin = java.qa.id) } apply(plugin = dokka.id) apply(plugin = gitSemVer.id) - apply(plugin = java.qa.id) - apply(plugin = multiJvmTesting.id) apply(plugin = kotlin.qa.id) apply(plugin = publishOnCentral.id) apply(plugin = shadowJar.id) @@ -62,17 +61,6 @@ allprojects { } apply(plugin = "distribution") - multiJvm { - jvmVersionForCompilation.set(minJavaVersion.toInt()) - maximumSupportedJvmVersion.set(latestJava) - if (isInCI && (isWindows || isMac)) { - /* - * Reduce time in CI by running on fewer JVMs on slower or more limited instances. - */ - testByDefaultWith(latestJava) - } - } - repositories { google() mavenCentral() @@ -86,8 +74,70 @@ allprojects { } // JVM PROJECTS CONFIGURATIONS - if (!project.isMultiplatform) { + // CODE QUALITY + + javaQA { + checkstyle { + additionalConfiguration.set( + """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """.trimIndent(), + ) + additionalSuppressions.set( + """ + + + """.trimIndent(), + ) + } + } + + multiJvm { + jvmVersionForCompilation.set(minJavaVersion.toInt()) + maximumSupportedJvmVersion.set(latestJava) + if (isInCI && (isWindows || isMac)) { + /* + * Reduce time in CI by running on fewer JVMs on slower or more limited instances. + */ + testByDefaultWith(latestJava) + } + } + dependencies { with(rootProject.libs) { compileOnly(spotbugs.annotations) @@ -204,58 +254,6 @@ allprojects { maxHeapSize = "1g" } - // CODE QUALITY - - javaQA { - checkstyle { - additionalConfiguration.set( - """ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - """.trimIndent(), - ) - additionalSuppressions.set( - """ - - - """.trimIndent(), - ) - } - } - tasks.withType().configureEach { reports { create("html") { enabled = true } From 15d44f8f886c0e849f5f8b29486149814e4cdce8 Mon Sep 17 00:00:00 2001 From: AngeloFilaseta Date: Thu, 1 Feb 2024 17:22:02 +0100 Subject: [PATCH 08/22] build: remove ktor plugin --- gradle/libs.versions.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 530d90a4d0..0164a2eaf4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -164,7 +164,6 @@ kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } kotlin-qa = "org.danilopianini.gradle-kotlin-qa:0.59.0" -ktor = { id = "io.ktor.plugin", version.ref = "ktor" } multiJvmTesting = "org.danilopianini.multi-jvm-test-plugin:0.5.5" publishOnCentral = "org.danilopianini.publish-on-central:5.0.22" scalafmt = "cz.alenkacz.gradle.scalafmt:1.16.2" From 03561591af71792e4599c7e696368c182b0e191f Mon Sep 17 00:00:00 2001 From: Tiziano Vuksan Date: Mon, 12 Feb 2024 11:30:53 +0100 Subject: [PATCH 09/22] feat(webgui): Added node inspection Now clicking on a node presents its properties, concentrations and reactions Added custom component for collapsible views Changes in the overall UI structure --- .idea/httpRequests/http-client.cookies | 0 .idea/httpRequests/http-requests-log.http | 0 .../resources/graphql/EnvironmentSub.graphql | 1 + .../resources/graphql/NodeQuery.graphql | 27 ++- .../boundary/launchers/GraphQLServer.kt | 2 +- alchemist-webgui/build.gradle.kts | 67 ++----- .../kotlin/graphql/api/EnvironmentApi.kt | 2 +- .../src/jsMain/kotlin/components/Content.kt | 73 +++---- .../components/content/CanvasHandlers.kt | 33 ++++ .../components/content/DrawingExtensions.kt | 116 +++++++++++ .../components/content/NodeProperties.kt | 164 ++++++++++++++++ .../components/content/SimulationContext.kt | 181 ++++-------------- .../content/SimulationIndicators.kt | 97 ++++++---- .../components/content/common/Collapsible.kt | 51 +++++ .../content/shared/CommonProperties.kt | 11 +- .../jsMain/kotlin/stores/EnvironmentStore.kt | 14 +- .../src/jsMain/kotlin/stores/NodeStore.kt | 33 ++++ .../kotlin/stores/actions/NodeStateAction.kt | 17 ++ .../kotlin/stores/reducers/NodeReducer.kt | 21 ++ .../states/NodeState.kt} | 6 +- .../stores/states/ScaleTranslateState.kt | 2 +- .../src/jsMain/kotlin/utils/RenderingUtils.kt | 26 +++ .../boundary/webgui/monitor/WebUIMonitor.kt | 74 +++++++ settings.gradle.kts | 4 +- 24 files changed, 724 insertions(+), 298 deletions(-) create mode 100644 .idea/httpRequests/http-client.cookies create mode 100644 .idea/httpRequests/http-requests-log.http create mode 100644 alchemist-webgui/src/jsMain/kotlin/components/content/CanvasHandlers.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/components/content/DrawingExtensions.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/components/content/NodeProperties.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/components/content/common/Collapsible.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/stores/NodeStore.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/stores/actions/NodeStateAction.kt create mode 100644 alchemist-webgui/src/jsMain/kotlin/stores/reducers/NodeReducer.kt rename alchemist-webgui/src/jsMain/kotlin/{components/navbar/NavTitle.kt => stores/states/NodeState.kt} (72%) create mode 100644 alchemist-webgui/src/jsMain/kotlin/utils/RenderingUtils.kt create mode 100644 alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/webgui/monitor/WebUIMonitor.kt diff --git a/.idea/httpRequests/http-client.cookies b/.idea/httpRequests/http-client.cookies new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.idea/httpRequests/http-requests-log.http b/.idea/httpRequests/http-requests-log.http new file mode 100644 index 0000000000..e69de29bb2 diff --git a/alchemist-graphql/src/commonMain/resources/graphql/EnvironmentSub.graphql b/alchemist-graphql/src/commonMain/resources/graphql/EnvironmentSub.graphql index db171f0962..ba4a4f66e1 100644 --- a/alchemist-graphql/src/commonMain/resources/graphql/EnvironmentSub.graphql +++ b/alchemist-graphql/src/commonMain/resources/graphql/EnvironmentSub.graphql @@ -2,6 +2,7 @@ subscription Environment { environment{ nodeToPos{ entries{ + id position{ coordinates } diff --git a/alchemist-graphql/src/commonMain/resources/graphql/NodeQuery.graphql b/alchemist-graphql/src/commonMain/resources/graphql/NodeQuery.graphql index 540056c223..0dbc7f491b 100644 --- a/alchemist-graphql/src/commonMain/resources/graphql/NodeQuery.graphql +++ b/alchemist-graphql/src/commonMain/resources/graphql/NodeQuery.graphql @@ -1,4 +1,29 @@ -query NodeQuery($id: Int! = 0) { +query Node($id: Int! = 0){ + + environment{ + nodeById(id: $id){ + id + moleculeCount + contents{ + size + entries{ + molecule{ + name + } + concentration + } + } + properties + reactions{ + inputContext + outputContext + node{ + id + } + } + } + } + nodePosition(nodeId: $id) { coordinates } diff --git a/alchemist-graphql/src/jvmMain/kotlin/it/unibo/alchemist/boundary/launchers/GraphQLServer.kt b/alchemist-graphql/src/jvmMain/kotlin/it/unibo/alchemist/boundary/launchers/GraphQLServer.kt index a797c6d63d..77f398ff68 100644 --- a/alchemist-graphql/src/jvmMain/kotlin/it/unibo/alchemist/boundary/launchers/GraphQLServer.kt +++ b/alchemist-graphql/src/jvmMain/kotlin/it/unibo/alchemist/boundary/launchers/GraphQLServer.kt @@ -33,7 +33,7 @@ import kotlinx.coroutines.sync.Semaphore * By default, the server is stopped after the simulation terminates. * This behavior can be changed by setting [teardownOnSimulationTermination] to false. */ -class GraphQLServer> @JvmOverloads constructor( +open class GraphQLServer> @JvmOverloads constructor( val environment: Environment, val host: String = DefaultGraphQLSettings.DEFAULT_HOST, val port: Int = DefaultGraphQLSettings.DEFAULT_PORT, diff --git a/alchemist-webgui/build.gradle.kts b/alchemist-webgui/build.gradle.kts index a6090aa02e..01ac2b7327 100644 --- a/alchemist-webgui/build.gradle.kts +++ b/alchemist-webgui/build.gradle.kts @@ -1,4 +1,5 @@ import Libs.alchemist +import Libs.incarnation import io.gitlab.arturbosch.detekt.Detekt import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig @@ -10,7 +11,6 @@ plugins { application alias(libs.plugins.kotest.multiplatform) alias(libs.plugins.kotlin.serialization) - alias(libs.plugins.ktor) alias(libs.plugins.graphql.server) alias(libs.plugins.graphql.client) @@ -22,33 +22,7 @@ repositories { mavenLocal() } -/*dependencies { - implementation(alchemist("graphql")) - implementation(alchemist("api")) - implementation(alchemist("full")) - - implementation(libs.apollo.runtime) - implementation(libs.kotlin.coroutines.core) - implementation(libs.kotlinx.serialization.json) - - implementation(libs.ktor.server.websockets) - implementation(libs.bundles.graphql.server) - implementation(libs.bundles.ktor.server) - - implementation(libs.kotest.runner) - implementation(libs.ktor.server.test.host) -}*/ - -/*application { - mainClass.set("it.unibo.alchemist.Alchemist") -}*/ - val task by tasks.register("runWEB") { - dependencies { - implementation(alchemist("graphql")) - implementation(alchemist("api")) - implementation(alchemist("full")) - } group = alchemistGroup description = "Launches simulation" @@ -60,18 +34,16 @@ val task by tasks.register("runWEB") { args("run", "C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/yaml/" + simulationFile.get()) - doFirst { - val libsDir = "C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/Alchemist fork/Alchemist/alchemist-graphql/build/libs/" - classpath( - sourceSets["main"].runtimeClasspath, - fileTree(libsDir) { - include("**/*.jar") - include("**/*.klib") - }, - ) - } - - // classpath(tasks.named("compileKotlinJvm"), configurations.named("jvmRuntimeClasspath")) + classpath( + tasks.named("compileKotlinJvm"), + configurations.named("jvmRuntimeClasspath"), + tasks.named("jsBrowserProductionWebpack").flatMap { it.outputs.files.elements }, + tasks.named("jsBrowserDistribution").flatMap { it.outputs.files.elements }, + project(":alchemist-graphql") + .tasks.named("generateAlchemist-graphqlApolloSources") + .flatMap { it.outputs.files.elements }, + ) + // dependsOn(tasks.named(":alchemist-graphql:generateAlchemist-graphqlApolloSources")) } kotlin { @@ -146,20 +118,15 @@ kotlin { } }*/ - /*val jvmMain by getting { + val jvmMain by getting { dependencies { - implementation(alchemist("graphql")) + implementation(incarnation("sapere")) + implementation(incarnation("protelis")) + implementation(alchemist("cognitive-agents")) + implementation("io.ktor:ktor-server-html-builder:2.3.8") implementation(libs.bundles.ktor.server) - implementation(libs.logback) - implementation(libs.resourceloader) - implementation(libs.apollo.runtime) - implementation(libs.korim) - implementation(libs.kotlin.stdlib) - implementation(libs.kotlinx.serialization.json) - implementation(libs.ktor.client.core) - implementation(libs.redux.kotlin.threadsafe) } - }*/ + } val jsMain by getting { dependencies { diff --git a/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt b/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt index dee336f847..e9fdb2dad5 100644 --- a/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt +++ b/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt @@ -21,7 +21,7 @@ import kotlinx.coroutines.flow.Flow object EnvironmentApi { - suspend fun retrieveQuery(nodeId: Int = 0): Deferred = coroutineScope { + suspend fun nodeQuery(nodeId: Int = 0): Deferred = coroutineScope { async { ClientConnection.client.query(NodeQuery(id = Optional.present(nodeId))).execute().data } diff --git a/alchemist-webgui/src/jsMain/kotlin/components/Content.kt b/alchemist-webgui/src/jsMain/kotlin/components/Content.kt index 4e763795a0..70396c1e92 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/Content.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/Content.kt @@ -9,23 +9,20 @@ package components +import components.content.NodeProperties import components.content.SimulationContext import components.content.SimulationIndicators import io.kvision.core.AlignItems import io.kvision.core.Background -import io.kvision.core.BoxShadow import io.kvision.core.Col import io.kvision.core.Color -import io.kvision.core.CssSize import io.kvision.core.FlexDirection import io.kvision.core.FlexWrap import io.kvision.core.JustifyContent -import io.kvision.core.UNIT import io.kvision.html.div import io.kvision.panel.SimplePanel import io.kvision.panel.flexPanel import io.kvision.panel.hPanel -import io.kvision.panel.vPanel import io.kvision.utils.perc import io.kvision.utils.px @@ -36,59 +33,45 @@ open class Content(className: String = "") : SimplePanel(className = className) // val offcanvas = offcanvas("Lorem ipsum", OffPlacement.END, dark = true) // offcanvas.show() - /*flexPanel( - FlexDirection.ROW, - FlexWrap.WRAP, + hPanel( + FlexWrap.NOWRAP, JustifyContent.CENTER, - AlignItems.START, + AlignItems.CENTER, spacing = 5, - ) {*/ - /*div(className = "nodes-list") { - bind(EnvironmentStore.store) { env -> - env.nodes.forEach { - div { +"position: ${it.position.coordinates}" } - } - } - }*/ + ) { + background = Background(color = Color.name(Col.LIGHTGRAY)) - hPanel( + add( + SimulationContext(className = "simulation-context").apply { + width = 1400.px + height = 900.px + }, + ) + + flexPanel( + FlexDirection.COLUMN, FlexWrap.NOWRAP, - JustifyContent.CENTER, + JustifyContent.START, AlignItems.START, spacing = 5, ) { - background = Background(color = Color.name(Col.LIGHTGRAY)) - + width = 520.px + height = 95.perc + // border = Border(width = 1.px, BorderStyle.SOLID, Color("#ff0000")) add( - SimulationContext(className = "simulation-context").apply{ - width = 1400.px - height = 900.px - } + SimulationIndicators(className = "simulation-indicators").apply { + width = 100.perc + height = 100.perc + }, ) add( - vPanel( - JustifyContent.CENTER, - AlignItems.START, - spacing = 5, - ) { - width = 520.px - add( - SimulationIndicators(className = "simulation-indicators").apply{ - width = 520.px - } - ) - div{ - flexGrow = 1 - height = 100.perc - borderRadius = CssSize(10, UNIT.px) - boxShadow = BoxShadow(0.px, 0.px, 5.px, 0.px, Color.rgba(0, 0, 0, (0.5 * 255).toInt())) - background = Background(color = Color.name(Col.WHITE)) - } - - } + NodeProperties(className = "node-properties").apply { + width = 100.perc + height = 100.perc + }, ) } - //} + } } } diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/CanvasHandlers.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/CanvasHandlers.kt new file mode 100644 index 0000000000..ff7d13cd90 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/CanvasHandlers.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package components.content + +import components.content.shared.CommonProperties +import org.w3c.dom.DOMRect +import org.w3c.dom.events.MouseEvent +import stores.EnvironmentStore +import stores.NodeStore +import utils.isMouseOverNodePosition + +fun findNode(event: MouseEvent, boundingRect: DOMRect) { + val state = CommonProperties.Observables.scaleTranslationStore.getState() + + val calcX = (event.clientX - boundingRect.left - state.translate.first) / state.scale + val calcY = (event.clientY - boundingRect.top - state.translate.second) / state.scale + + val radius = CommonProperties.Observables.nodesRadius.value * 1 / state.scale + + val node = EnvironmentStore.store.getState().nodes.firstOrNull { entry -> + isMouseOverNodePosition(calcX, calcY, entry.position.coordinates[0], entry.position.coordinates[1], radius) + } + if (node == null) return + + NodeStore.nodeById(node.id) +} diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/DrawingExtensions.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/DrawingExtensions.kt new file mode 100644 index 0000000000..ee4646c865 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/DrawingExtensions.kt @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package components.content + +import components.content.shared.CommonProperties +import org.w3c.dom.CanvasRenderingContext2D +import stores.EnvironmentStore +import kotlin.math.PI + +private object GridProperties { + const val LINE_WIDTH = 1.0 + const val STROKE_COLOR = "#ADADAD" + const val CELL_SIZE = 10 +} + +fun CanvasRenderingContext2D.drawNode( + position: Pair, + color: String = CommonProperties.RenderProperties.DEFAULT_NODE_COLOR, +) { + beginPath() + arc( + position.first, + position.second, + CommonProperties.Observables.nodesRadius.value * 1 / + CommonProperties.Observables.scaleTranslationStore.getState().scale, + 0.0, + 2 * PI, + false, + ) + fillStyle = color + fill() + closePath() +} + +fun CanvasRenderingContext2D.redrawNodes( + scale: Double = CommonProperties.Observables.scaleTranslationStore.getState().scale, + translation: Pair = CommonProperties.Observables.scaleTranslationStore.getState().translate, +) { + println("Function[Redraw]: Redrawing function") + val translationScaled = Pair( + translation.first * 1 / scale, + translation.second * 1 / scale, + ) + + scale(scale, scale) + translate(translationScaled.first, translationScaled.second) + + clearRect( + -translationScaled.first, + -translationScaled.second, + CommonProperties.RenderProperties.DEFAULT_WIDTH, + CommonProperties.RenderProperties.DEFAULT_HEIGHT, + ) + + drawGrid() + + beginPath() + moveTo(0.0, 0.0) + arc( + 0.0, + 0.0, + CommonProperties.Observables.nodesRadius.value * 1 / + CommonProperties.Observables.scaleTranslationStore.getState().scale, + 0.0, + 2 * PI, + false, + ) + fillStyle = "#00ff00" + fill() + closePath() + + EnvironmentStore.store.getState().nodes.forEach { + drawNode(Pair(it.position.coordinates[0], it.position.coordinates[1])) // randomColor()) + } + + setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) +} + +fun CanvasRenderingContext2D.drawGrid() { + lineWidth = GridProperties.LINE_WIDTH / CommonProperties.Observables.scaleTranslationStore.getState().scale + strokeStyle = GridProperties.STROKE_COLOR + + val left = -CommonProperties.RenderProperties.DEFAULT_WIDTH + val top = -CommonProperties.RenderProperties.DEFAULT_HEIGHT + val right = CommonProperties.RenderProperties.DEFAULT_WIDTH + val bottom = CommonProperties.RenderProperties.DEFAULT_HEIGHT + + clearRect(left, top, right - left, bottom - top) + beginPath() + + generateSequence(left.toInt()) { it + GridProperties.CELL_SIZE } + .takeWhile { it < right.toInt() + GridProperties.CELL_SIZE } + .map(Int::toDouble) + .forEach { + moveTo(it, top) + lineTo(it, bottom) + } + + generateSequence(top.toInt()) { it + GridProperties.CELL_SIZE } + .takeWhile { it < bottom.toInt() + GridProperties.CELL_SIZE } + .map(Int::toDouble) + .forEach { + moveTo(left, it) + lineTo(right, it) + } + + stroke() + closePath() +} diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/NodeProperties.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/NodeProperties.kt new file mode 100644 index 0000000000..b615b90419 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/NodeProperties.kt @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package components.content + +import components.content.common.collapsible +import components.content.shared.CommonProperties +import io.kvision.core.AlignContent +import io.kvision.core.AlignItems +import io.kvision.core.Background +import io.kvision.core.BoxShadow +import io.kvision.core.Col +import io.kvision.core.Color +import io.kvision.core.CssSize +import io.kvision.core.FlexDirection +import io.kvision.core.FlexWrap +import io.kvision.core.JustifyContent +import io.kvision.core.OverflowWrap +import io.kvision.core.UNIT +import io.kvision.html.div +import io.kvision.html.h5 +import io.kvision.panel.SimplePanel +import io.kvision.panel.flexPanel +import io.kvision.panel.hPanel +import io.kvision.panel.vPanel +import io.kvision.state.bind +import io.kvision.utils.perc +import io.kvision.utils.px +import stores.NodeStore + +class NodeProperties(className: String = "") : SimplePanel(className = className) { + + init { + borderRadius = CssSize(10, UNIT.px) + boxShadow = BoxShadow(0.px, 0.px, 5.px, 0.px, Color.rgba(0, 0, 0, (0.4 * 255).toInt())) + background = Background(color = Color.name(Col.WHITE)) + + flexPanel( + FlexDirection.ROW, + FlexWrap.NOWRAP, + JustifyContent.CENTER, + AlignItems.CENTER, + ) { + vPanel { + justifyContent = JustifyContent.CENTER + spacing = 5 + width = 95.perc + + h5 { + +"Node evaluation" + width = 100.perc + height = 100.perc + } + + flexPanel( + FlexDirection.ROW, + FlexWrap.NOWRAP, + JustifyContent.CENTER, + AlignItems.STRETCH, + AlignContent.CENTER, + spacing = 5, + className = "nodeinfo-header", + ) { + div { + width = 50.px + height = 50.px + borderRadius = CssSize(height!!.first.toDouble() * 0.5, UNIT.px) + background = Background(Color(CommonProperties.RenderProperties.DEFAULT_NODE_COLOR)) + overflowWrap = OverflowWrap.ANYWHERE + } + + div { + width = 100.perc + overflowWrap = OverflowWrap.ANYWHERE + }.bind(NodeStore.nodeStore) { + +"${it.node?.environment?.nodeById?.id}" + } + + div { + width = 100.perc + overflowWrap = OverflowWrap.ANYWHERE + }.bind(NodeStore.nodeStore) { + +"${it.node?.nodePosition?.coordinates?.get(0)}, ${it.node?.nodePosition?.coordinates?.get(1)}" + } + } + + collapsible("properties-collapsible", "Properties") { + bind(NodeStore.nodeStore) { state -> + state.node?.environment?.nodeById?.properties?.forEach { + div { + width = 100.perc + overflowWrap = OverflowWrap.ANYWHERE + +it + } + } + } + } + + collapsible("contents-collapsible", "Contents") { + bind(NodeStore.nodeStore) { state -> + state.node?.environment?.nodeById?.contents?.entries?.forEach { + + hPanel( + FlexWrap.NOWRAP, + JustifyContent.CENTER, + AlignItems.CENTER, + spacing = 5, + ) { + div { + width = 50.perc + overflowWrap = OverflowWrap.ANYWHERE + +it.molecule.name + } + div { + width = 50.perc + overflowWrap = OverflowWrap.ANYWHERE + +it.concentration + } + } + } + } + } + + collapsible("reactions-collapsible", "Reactions") { + bind(NodeStore.nodeStore) { state -> + state.node?.environment?.nodeById?.reactions?.forEach { + + flexPanel( + FlexDirection.ROW, + FlexWrap.NOWRAP, + JustifyContent.CENTER, + AlignItems.STRETCH, + AlignContent.CENTER, + spacing = 5, + ) { + div { + width = 50.perc + overflowWrap = OverflowWrap.ANYWHERE + +it.inputContext.name + } + div { + width = 50.perc + overflowWrap = OverflowWrap.ANYWHERE + +it.outputContext.name + } + div { + width = 100.perc + overflowWrap = OverflowWrap.ANYWHERE + it.node.id + } + } + } + } + } + } + } + } +} diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt index 65c8c156b2..6f2cd12337 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt @@ -10,12 +10,8 @@ package components.content import components.content.shared.CommonProperties -import graphql.api.EnvironmentApi -import io.kvision.core.AlignContent import io.kvision.core.AlignItems import io.kvision.core.Background -import io.kvision.core.Border -import io.kvision.core.BorderStyle import io.kvision.core.BoxShadow import io.kvision.core.Col import io.kvision.core.Color @@ -25,6 +21,7 @@ import io.kvision.core.FlexDirection import io.kvision.core.FlexWrap import io.kvision.core.JustifyContent import io.kvision.core.UNIT +import io.kvision.core.onClick import io.kvision.core.onEvent import io.kvision.html.canvas import io.kvision.panel.SimplePanel @@ -32,154 +29,46 @@ import io.kvision.panel.flexPanel import io.kvision.state.bind import io.kvision.utils.perc import io.kvision.utils.px -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import org.w3c.dom.CanvasRenderingContext2D import org.w3c.dom.DOMRect import stores.EnvironmentStore -import stores.actions.EnvironmentStateAction import stores.actions.ScaleTranslateAction -import kotlin.math.PI -import kotlin.math.ceil -import kotlin.random.Random class SimulationContext(className: String = "") : SimplePanel(className = className) { + private var translatePos: Pair = Pair(0.0, 0.0) - data class Node(val id: String, val x: Double, val y: Double, val color: String) - - private val nodes = List(50) { index -> - Node("${index + 1}", Random.nextDouble(0.0, 400.0), Random.nextDouble(0.0, 200.0), randomColor()) - } - - private fun randomColor(): String { - val letters = "0123456789ABCDEF" - var color = "#" - repeat(6) { color += letters[Random.nextInt(15)] } - return color - } - - private fun CanvasRenderingContext2D.drawNode( - position: Pair, - color: String = CommonProperties.RenderProperties.DEFAULT_NODE_COLOR, - ) { - beginPath() - arc( - position.first, - position.second, - CommonProperties.Observables.nodesRadius.value * 1 / - CommonProperties.Observables.scaleTranslationStore.getState().scale, - 0.0, - 2 * PI, - false, + private var startDragOffset: Pair = + Pair( + CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble(), + CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble(), ) - fillStyle = color - fill() - closePath() - } - - private fun CanvasRenderingContext2D.redrawNodes( - scale: Double = CommonProperties.Observables.scaleTranslationStore.getState().scale, - translation: Pair = CommonProperties.Observables.scaleTranslationStore.getState().translate, - ) { - println("Function[Redraw]: Redrawing function") - val translationScaled = Pair( - translation.first * 1 / scale, - translation.second * 1 / scale, - ) - - scale(scale, scale) - translate(translationScaled.first, translationScaled.second) - - clearRect( - -translationScaled.first, - -translationScaled.second, - CommonProperties.RenderProperties.DEFAULT_WIDTH, - CommonProperties.RenderProperties.DEFAULT_HEIGHT, - ) - - drawGrid() - - /*EnvironmentStore.store.getState().nodes.forEach { - drawNode(Pair(it.position.coordinates[0], it.position.coordinates[1]), randomColor()) - }*/ - - nodes.forEach { - drawNode(Pair(it.x, it.y), it.color) - } - - setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) - } - - private fun CanvasRenderingContext2D.drawGrid(){ - strokeStyle = "#ccc" - lineWidth = 1.0 / CommonProperties.Observables.scaleTranslationStore.getState().scale - - val stepP = 10 - val left = 0.5 - ceil(CommonProperties.RenderProperties.DEFAULT_WIDTH / stepP) * stepP - val top = 0.5 - ceil(CommonProperties.RenderProperties.DEFAULT_HEIGHT / stepP) * stepP - val right = 2 * CommonProperties.RenderProperties.DEFAULT_WIDTH - val bottom = 2 * CommonProperties.RenderProperties.DEFAULT_HEIGHT - clearRect(left, top, right - left, bottom - top) - beginPath() - for (x in left.toInt() until right.toInt() + stepP step stepP) { - moveTo(x.toDouble(), top) - lineTo(x.toDouble(), bottom) - } - for (y in top.toInt() until bottom.toInt() + stepP step stepP) { - moveTo(left, y.toDouble()) - lineTo(right, y.toDouble()) - } - strokeStyle = "#ADADAD" - stroke() - } init { + // border = Border(width = 1.px, BorderStyle.SOLID, Color("#ff0000")) - border = Border(width = 1.px, BorderStyle.SOLID, Color("#ff0000")) - - CoroutineScope(Dispatchers.Default).launch { - println("COROUTINE[environmentSubscription]: Environmnent subscription started") - EnvironmentApi.environMentSubScription().collect { - // println("A") - EnvironmentStore.store.dispatch(EnvironmentStateAction.AddAllNodes(it.data?.environment?.nodeToPos!!.entries)) - } - println("COROUTINE[environmentSubscription]: Environmnent subscription ended") - } + EnvironmentStore.callEnvironmentSubscription() flexPanel( FlexDirection.COLUMN, FlexWrap.NOWRAP, JustifyContent.CENTER, AlignItems.CENTER, - AlignContent.CENTER ) { height = 100.perc - canvas(className = "environment-renderer") { + canvas(className = "environment-renderer") { + + lateinit var boundingRect: DOMRect + var mouseIsDown = false + canvasWidth = CommonProperties.RenderProperties.DEFAULT_WIDTH.toInt() canvasHeight = CommonProperties.RenderProperties.DEFAULT_HEIGHT.toInt() borderRadius = CssSize(10, UNIT.px) - boxShadow = BoxShadow(0.px, 0.px, 5.px, 0.px, Color.rgba(0, 0, 0, (0.5 * 255).toInt())) + boxShadow = BoxShadow(0.px, 0.px, 5.px, 0.px, Color.rgba(0, 0, 0, (0.4 * 255).toInt())) background = Background(color = Color.name(Col.WHITE)) - var mouseIsDown = false - - var translatePos: Pair - - var startDragOffset: Pair = - Pair( - CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble(), - CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble(), - ) - lateinit var boundingRect: DOMRect - - addAfterCreateHook { - boundingRect = getElement()!!.getBoundingClientRect() - bind(CommonProperties.Observables.scaleTranslationStore) { state -> context2D.redrawNodes(state.scale, state.translate) } @@ -194,26 +83,32 @@ class SimulationContext(className: String = "") : SimplePanel(className = classN } } + addAfterInsertHook { + boundingRect = getElement()!!.getBoundingClientRect() + } + + onClick { + findNode(it, boundingRect) + } + onEvent { + mousedown = { e -> - cursor = Cursor.GRABBING + cursor = Cursor.POINTER mouseIsDown = true startDragOffset = Pair( e.clientX - CommonProperties.Observables.scaleTranslationStore.getState().translate.first, - e.clientY - CommonProperties.Observables.scaleTranslationStore.getState().translate.second + e.clientY - CommonProperties.Observables.scaleTranslationStore.getState().translate.second, ) } mousemove = { e -> if (mouseIsDown) { - - translatePos = Pair( - e.clientX - startDragOffset.first, - e.clientY - startDragOffset.second - ) + cursor = Cursor.GRABBING + translatePos = Pair(e.clientX - startDragOffset.first, e.clientY - startDragOffset.second) CommonProperties.Observables.scaleTranslationStore.dispatch( - ScaleTranslateAction.SetTranslation(translatePos) + ScaleTranslateAction.SetTranslation(translatePos), ) } } @@ -231,15 +126,15 @@ class SimulationContext(className: String = "") : SimplePanel(className = classN nextScale >= CommonProperties.RenderProperties.MIN_SCALE ) { val currentState = CommonProperties.Observables.scaleTranslationStore.getState() - val scaleChangeFactor = nextScale / currentState.scale - val translationChangeX = (1 - scaleChangeFactor) * (e.clientX - boundingRect.left - currentState.translate.first) - val translationChangeY = (1 - scaleChangeFactor) * (e.clientY - boundingRect.top - currentState.translate.second) - translatePos = Pair( - translationChangeX, - translationChangeY - ) + val translationChangeX = + (1 - scaleChangeFactor) * (e.clientX - boundingRect.left - currentState.translate.first) + + val translationChangeY = + (1 - scaleChangeFactor) * (e.clientY - boundingRect.top - currentState.translate.second) + + translatePos = Pair(translationChangeX, translationChangeY) CommonProperties.Observables.scaleTranslationStore.dispatch( ScaleTranslateAction.SetScale(nextScale), @@ -249,9 +144,9 @@ class SimulationContext(className: String = "") : SimplePanel(className = classN ScaleTranslateAction.SetTranslation( Pair( currentState.translate.first + translationChangeX, - currentState.translate.second + translationChangeY - ) - ) + currentState.translate.second + translationChangeY, + ), + ), ) } } diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt index 365f51f4b4..af539ba76e 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt @@ -10,16 +10,20 @@ package components.content import components.content.shared.CommonProperties +import io.kvision.core.AlignItems import io.kvision.core.Background -import io.kvision.core.Border -import io.kvision.core.BorderStyle +import io.kvision.core.BoxShadow import io.kvision.core.Col import io.kvision.core.Color +import io.kvision.core.FlexDirection +import io.kvision.core.FlexWrap +import io.kvision.core.JustifyContent import io.kvision.core.onInput import io.kvision.form.number.range import io.kvision.html.div import io.kvision.html.p import io.kvision.panel.SimplePanel +import io.kvision.panel.flexPanel import io.kvision.panel.vPanel import io.kvision.progress.progress import io.kvision.progress.progressNumeric @@ -31,53 +35,66 @@ import korlibs.math.roundDecimalPlaces class SimulationIndicators(className: String = "") : SimplePanel(className = className) { init { - border = Border(width = 1.px, BorderStyle.SOLID, Color("#ff0000")) - height = 100.perc + boxShadow = BoxShadow(0.px, 0.px, 5.px, 0.px, Color.rgba(0, 0, 0, (0.4 * 255).toInt())) + background = Background(Color.name(Col.WHITE)) + borderRadius = 10.px + flexPanel( + FlexDirection.ROW, + FlexWrap.NOWRAP, + JustifyContent.CENTER, + AlignItems.CENTER, + ) { + vPanel { + justifyContent = JustifyContent.CENTER + spacing = 5 + width = 95.perc - vPanel { - height = 95.perc - width = 95.perc - spacing = 5 - borderRadius = 10.px - background = Background(color = Color.name(Col.WHITE)) - border = Border(width = 2.px, BorderStyle.SOLID, Color("#A3A3A3")) + div().bind(CommonProperties.Observables.scaleTranslationStore) { state -> + +"Scale: ${state.scale.roundDecimalPlaces(2)}" + } - div().bind(CommonProperties.Observables.scaleTranslationStore) { state -> - +"Scale: ${state.scale}" - } + div().bind(CommonProperties.Observables.scaleTranslationStore) { state -> - div().bind(CommonProperties.Observables.scaleTranslationStore) { state -> - +"Translation: ${state.translate.first}, ${state.translate.second}" - } + div { + +"X translation: ${state.translate.first.roundDecimalPlaces(2)}" + } - range { - label = "Node radius $min - $max" - min = CommonProperties.RenderProperties.DEFUALT_NODE_RADIUS - max = CommonProperties.RenderProperties.DEFUALT_NODE_RADIUS * 20 - step = 1.0 - value = CommonProperties.RenderProperties.DEFUALT_NODE_RADIUS - onInput { - CommonProperties.Observables.nodesRadius.value = getValue()!!.toDouble() + div { + +"Y translation: ${state.translate.second.roundDecimalPlaces(2)}" + } } - } - div().bind(CommonProperties.Observables.nodesRadius) { - +"Node radius: ${CommonProperties.Observables.nodesRadius.value}" - } + range { + label = "Node radius: $min ($min - $max)" + min = CommonProperties.RenderProperties.DEFUALT_NODE_RADIUS + max = CommonProperties.RenderProperties.DEFUALT_NODE_RADIUS * 10 + step = 1.0 + value = CommonProperties.RenderProperties.DEFUALT_NODE_RADIUS + onInput { + CommonProperties.Observables.nodesRadius.value = getValue()!!.toDouble() + } + }.bind(CommonProperties.Observables.nodesRadius) { + label = "Node radius: ${CommonProperties.Observables.nodesRadius.value} ($min - $max)" + } - p(className = "scale-value").bind(CommonProperties.Observables.scaleTranslationStore) { state -> - +"Zoom: ${((state.scale/CommonProperties.RenderProperties.MAX_SCALE) * 100).roundDecimalPlaces(0)}%" - } + /*div().bind(CommonProperties.Observables.nodesRadius) { + +"Node radius: ${CommonProperties.Observables.nodesRadius.value}" + }*/ + + p(className = "scale-value").bind(CommonProperties.Observables.scaleTranslationStore) { state -> + +"Zoom: ${((state.scale/CommonProperties.RenderProperties.MAX_SCALE) * 100).roundDecimalPlaces(0)}%" + } - progress( - min = CommonProperties.RenderProperties.MIN_SCALE, - max = CommonProperties.RenderProperties.MAX_SCALE, - ) { - progressNumeric { - striped = false + progress( + min = CommonProperties.RenderProperties.MIN_SCALE, + max = CommonProperties.RenderProperties.MAX_SCALE, + ) { + progressNumeric { + striped = false + } + }.getFirstProgressBar()!!.bind(CommonProperties.Observables.scaleTranslationStore) { state -> + value = state.scale } - }.getFirstProgressBar()!!.bind(CommonProperties.Observables.scaleTranslationStore) { state -> - value = state.scale } } } diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/common/Collapsible.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/common/Collapsible.kt new file mode 100644 index 0000000000..c294caef84 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/common/Collapsible.kt @@ -0,0 +1,51 @@ +package components.content.common + +import io.kvision.core.Container +import io.kvision.core.TextAlign +import io.kvision.html.ButtonStyle +import io.kvision.html.button +import io.kvision.panel.SimplePanel +import io.kvision.panel.simplePanel +import io.kvision.panel.vPanel +import io.kvision.utils.perc + +fun Container.collapsible( + className: String = "", + title: String = "", + content: (SimplePanel.() -> Unit)? = null, +) { + vPanel(className = className, spacing = 10) { + width = 100.perc + height = 100.perc + val contentPanel = simplePanel(className = "collapsible-content", init = content).apply { + width = 100.perc + hide() + } + button(className = "collapsible-button", text = title) { + textAlign = TextAlign.LEFT + + style = when (contentPanel.visible) { + true -> ButtonStyle.LIGHT + false -> ButtonStyle.DARK + } + + icon = when (contentPanel.visible) { + true -> "fa-solid fa-caret-up" + false -> "fa-solid fa-caret-down" + } + + onClick { + if (contentPanel.visible) { + contentPanel.hide() + style = ButtonStyle.DARK + icon = "fa-solid fa-caret-down" + } else { + contentPanel.show() + style = ButtonStyle.LIGHT + icon = "fa-solid fa-caret-up" + } + } + } + add(contentPanel) + } +} diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt index 6554709b72..e4b99453e2 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt @@ -13,20 +13,21 @@ import io.kvision.redux.createTypedReduxStore import io.kvision.state.ObservableValue import stores.reducers.scaleTranslateReducer import stores.states.ScaleTranslateState +import utils.randomColor class CommonProperties { object RenderProperties { - private const val SCALE_ITERATIONS = 25 + private const val SCALE_ITERATIONS = 100 const val DEFUALT_NODE_RADIUS = 10.0 const val DEFAULT_HEIGHT = 875.0 - const val DEFAULT_WIDTH = 1375.0 + const val DEFAULT_WIDTH = 1350.0 // 1375.0 const val DEFAULT_SCALE = 1.0 const val DEFAULT_START_POSITION = 0 - const val DEFAULT_SCALE_RATIO = 0.8 - const val DEFAULT_NODE_COLOR = "#FF0000" + const val DEFAULT_SCALE_RATIO = 0.9 const val MIN_SCALE = 1.0 - const val MAX_SCALE = MIN_SCALE * (1/DEFAULT_SCALE_RATIO) * SCALE_ITERATIONS + const val MAX_SCALE = MIN_SCALE * (1 / DEFAULT_SCALE_RATIO) * SCALE_ITERATIONS + val DEFAULT_NODE_COLOR = randomColor() } object Observables { diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt b/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt index 55ce9b2aff..ddf8dfdaa0 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt @@ -11,7 +11,8 @@ package stores import graphql.api.EnvironmentApi import io.kvision.redux.createTypedReduxStore -import kotlinx.coroutines.MainScope +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import stores.actions.EnvironmentStateAction import stores.reducers.environmentReducer @@ -22,13 +23,12 @@ object EnvironmentStore { val store = createTypedReduxStore(::environmentReducer, EnvironmentState(mutableListOf())) fun callEnvironmentSubscription() { - MainScope().launch { - println("COROUTINE[callEnvironmentSubscription]: Started") - EnvironmentApi.environMentSubScription().collect { response -> - - store.dispatch(EnvironmentStateAction.AddAllNodes(response.data!!.environment.nodeToPos.entries)) + CoroutineScope(Dispatchers.Default).launch { + println("COROUTINE[callEnvironmentSubscription]: Environmnent subscription started") + EnvironmentApi.environMentSubScription().collect { + store.dispatch(EnvironmentStateAction.AddAllNodes(it.data?.environment?.nodeToPos!!.entries)) } - println("COROUTINE[callEnvironmentSubscription]: Ended") + println("COROUTINE[callEnvironmentSubscription]: Environmnent subscription ended") } } } diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/NodeStore.kt b/alchemist-webgui/src/jsMain/kotlin/stores/NodeStore.kt new file mode 100644 index 0000000000..1dacf795a1 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/stores/NodeStore.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package stores + +import graphql.api.EnvironmentApi +import io.kvision.redux.createTypedReduxStore +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.launch +import stores.actions.NodeStateAction +import stores.reducers.nodeReducer +import stores.states.NodeState + +object NodeStore { + + val nodeStore = createTypedReduxStore(::nodeReducer, NodeState()) + + fun nodeById(nodeId: Int = 0) { + MainScope().launch { + println("COROUTINE[nodeById]: Started") + val result = EnvironmentApi.nodeQuery(nodeId).await() + + nodeStore.dispatch(NodeStateAction.SetNode(result)) + println("COROUTINE[nodeById]: Ended") + } + } +} diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/actions/NodeStateAction.kt b/alchemist-webgui/src/jsMain/kotlin/stores/actions/NodeStateAction.kt new file mode 100644 index 0000000000..9bb0bc9dcc --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/stores/actions/NodeStateAction.kt @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package stores.actions + +import io.kvision.redux.RAction +import it.unibo.alchemist.boundary.graphql.client.NodeQuery + +sealed class NodeStateAction : RAction { + data class SetNode(val node: NodeQuery.Data?) : NodeStateAction() +} diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/reducers/NodeReducer.kt b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/NodeReducer.kt new file mode 100644 index 0000000000..9e3bd52362 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/NodeReducer.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package stores.reducers + +import stores.actions.NodeStateAction +import stores.states.NodeState + +fun nodeReducer(state: NodeState, action: NodeStateAction): NodeState { + when (action) { + is NodeStateAction.SetNode -> { + return state.copy(node = action.node) + } + } +} diff --git a/alchemist-webgui/src/jsMain/kotlin/components/navbar/NavTitle.kt b/alchemist-webgui/src/jsMain/kotlin/stores/states/NodeState.kt similarity index 72% rename from alchemist-webgui/src/jsMain/kotlin/components/navbar/NavTitle.kt rename to alchemist-webgui/src/jsMain/kotlin/stores/states/NodeState.kt index f387c8c57b..91a076d572 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/navbar/NavTitle.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/states/NodeState.kt @@ -7,6 +7,8 @@ * as described in the file LICENSE in the Alchemist distribution's top directory. */ -package components.navbar +package stores.states -class NavTitle +import it.unibo.alchemist.boundary.graphql.client.NodeQuery + +data class NodeState(val node: NodeQuery.Data? = null) diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/states/ScaleTranslateState.kt b/alchemist-webgui/src/jsMain/kotlin/stores/states/ScaleTranslateState.kt index 03bc95abc1..8f45147902 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/states/ScaleTranslateState.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/states/ScaleTranslateState.kt @@ -12,6 +12,6 @@ package stores.states import components.content.shared.CommonProperties.RenderProperties.DEFAULT_SCALE data class ScaleTranslateState( - var scale: Double = DEFAULT_SCALE.toDouble(), + var scale: Double = DEFAULT_SCALE, var translate: Pair = Pair(0.0, 0.0), ) diff --git a/alchemist-webgui/src/jsMain/kotlin/utils/RenderingUtils.kt b/alchemist-webgui/src/jsMain/kotlin/utils/RenderingUtils.kt new file mode 100644 index 0000000000..1db7839a16 --- /dev/null +++ b/alchemist-webgui/src/jsMain/kotlin/utils/RenderingUtils.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package utils + +import kotlin.random.Random + +fun isMouseOverNodePosition(mouseX: Double, mouseY: Double, nodeX: Double, nodeY: Double, radius: Double): Boolean { + val dx = mouseX - nodeX + val dy = mouseY - nodeY + + return dx * dx + dy * dy <= radius * radius +} + +fun randomColor(): String { + val letters = "0123456789ABCDEF" + var color = "#" + repeat(6) { color += letters[Random.nextInt(15)] } + return color +} diff --git a/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/webgui/monitor/WebUIMonitor.kt b/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/webgui/monitor/WebUIMonitor.kt new file mode 100644 index 0000000000..ea3526cc7c --- /dev/null +++ b/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/webgui/monitor/WebUIMonitor.kt @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +package it.unibo.alchemist.boundary.webgui.monitor + +import com.google.common.io.Resources +import io.ktor.http.ContentType +import io.ktor.http.HttpMethod +import io.ktor.server.application.call +import io.ktor.server.application.install +import io.ktor.server.engine.embeddedServer +import io.ktor.server.http.content.staticResources +import io.ktor.server.netty.Netty +import io.ktor.server.plugins.cors.routing.CORS +import io.ktor.server.response.respondText +import io.ktor.server.routing.get +import io.ktor.server.routing.routing +import it.unibo.alchemist.model.Environment +import it.unibo.alchemist.model.Position +import korlibs.io.async.launch +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.internal.wait +import it.unibo.alchemist.boundary.launchers.GraphQLServer as GraphQLServer + +/* + * Copyright (C) 2010-2024, Danilo Pianini and contributors + * listed, for each module, in the respective subproject's build.gradle.kts file. + * + * This file is part of Alchemist, and is distributed under the terms of the + * GNU General Public License, with a linking exception, + * as described in the file LICENSE in the Alchemist distribution's top directory. + */ + +class WebUIMonitor> ( + environment: Environment, +) : GraphQLServer(environment) { + private val serverDispatcher: CoroutineDispatcher = Dispatchers.Default + + override fun initialized(environment: Environment) { + super.initialized(environment) + startWebServer() + } + + private fun startWebServer() { + launch(serverDispatcher) { + embeddedServer(Netty, port = 9090) { + install(CORS) { + allowSameOrigin + allowCredentials = true + allowMethod(HttpMethod.Get) + allowMethod(HttpMethod.Post) + anyHost() + } + + routing { + get("/") { + val resource = Resources.getResource("index.html") + call.respondText(resource.readText(), ContentType.Text.Html) + } + + staticResources("/", "") + } + }.start(wait = false) + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index f0429d6d56..ca3371dd5e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -55,8 +55,8 @@ gradleEnterprise { gitHooks { commitMsg { conventionalCommits() } - /*preCommit { + preCommit { tasks("ktlintCheck", "checkScalafmt", "--parallel") - }*/ + } createHooks(overwriteExisting = true) } From 5133f37ffe370ff380c1b54322178fd1d08be203 Mon Sep 17 00:00:00 2001 From: Tiziano Vuksan Date: Thu, 15 Feb 2024 18:45:36 +0100 Subject: [PATCH 10/22] fix(webgui): Fixed subscription total block of the UI chore(javadoc): Added javadoc for all classes and methods chore(webgui): Cleaned code --- .../graphql/EnvironmentQuery.graphql | 12 +++ .../boundary/launchers/GraphQLServer.kt | 2 - alchemist-webgui/build.gradle.kts | 45 ++-------- .../kotlin/client/ClientConnection.kt | 6 ++ .../kotlin/graphql/api/EnvironmentApi.kt | 25 +++++- .../graphql/api/SimulationControlApi.kt | 45 +++++----- .../src/commonMain/kotlin/utils/SimState.kt | 23 +++-- alchemist-webgui/src/jsMain/kotlin/Client.kt | 4 + .../src/jsMain/kotlin/components/Appbar.kt | 27 +++--- .../src/jsMain/kotlin/components/Content.kt | 24 ++--- .../components/content/CanvasHandlers.kt | 13 ++- .../components/content/DrawingExtensions.kt | 90 ++++++++++++------- .../components/content/NodeProperties.kt | 54 ++++++----- .../components/content/SimulationContext.kt | 80 ++++++++++------- .../content/SimulationIndicators.kt | 49 ++++++---- .../components/content/common/Collapsible.kt | 7 ++ .../content/shared/CommonProperties.kt | 18 +++- .../kotlin/components/navbar/PlayButton.kt | 19 ++-- .../jsMain/kotlin/stores/EnvironmentStore.kt | 47 ++++++++-- .../src/jsMain/kotlin/stores/NodeStore.kt | 13 ++- .../jsMain/kotlin/stores/SimulationStatus.kt | 12 ++- .../stores/actions/EnvironmentStateAction.kt | 14 +++ .../kotlin/stores/actions/NodeStateAction.kt | 9 ++ .../stores/actions/ScaleTranslateAction.kt | 14 +++ .../kotlin/stores/actions/SimulationAction.kt | 9 ++ .../stores/reducers/EnvironmentReducer.kt | 8 +- .../kotlin/stores/reducers/NodeReducer.kt | 6 ++ .../stores/reducers/ScaleTranslateReducer.kt | 6 ++ .../stores/reducers/SimulationReducer.kt | 6 ++ .../kotlin/stores/states/EnvironmentState.kt | 16 +++- .../jsMain/kotlin/stores/states/NodeState.kt | 5 ++ .../stores/states/ScaleTranslateState.kt | 6 ++ .../kotlin/stores/states/SimulationState.kt | 5 ++ .../src/jsMain/kotlin/ui/AppMain.kt | 9 ++ .../src/jsMain/kotlin/utils/RenderingUtils.kt | 15 ++++ .../boundary/server/modules/InstallModule.kt | 14 ++- .../boundary/server/modules/RoutingModule.kt | 21 ++++- .../boundary/server/routes/MainRoute.kt | 28 ------ .../boundary/webgui/monitor/WebUIMonitor.kt | 68 +++++++------- 39 files changed, 582 insertions(+), 292 deletions(-) create mode 100644 alchemist-graphql/src/commonMain/resources/graphql/EnvironmentQuery.graphql delete mode 100644 alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/server/routes/MainRoute.kt diff --git a/alchemist-graphql/src/commonMain/resources/graphql/EnvironmentQuery.graphql b/alchemist-graphql/src/commonMain/resources/graphql/EnvironmentQuery.graphql new file mode 100644 index 0000000000..e3f876e964 --- /dev/null +++ b/alchemist-graphql/src/commonMain/resources/graphql/EnvironmentQuery.graphql @@ -0,0 +1,12 @@ +query EnvironmentQuery { + environment{ + nodeToPos{ + entries{ + id + position{ + coordinates + } + } + } + } +} \ No newline at end of file diff --git a/alchemist-graphql/src/jvmMain/kotlin/it/unibo/alchemist/boundary/launchers/GraphQLServer.kt b/alchemist-graphql/src/jvmMain/kotlin/it/unibo/alchemist/boundary/launchers/GraphQLServer.kt index 77f398ff68..d23cad0c53 100644 --- a/alchemist-graphql/src/jvmMain/kotlin/it/unibo/alchemist/boundary/launchers/GraphQLServer.kt +++ b/alchemist-graphql/src/jvmMain/kotlin/it/unibo/alchemist/boundary/launchers/GraphQLServer.kt @@ -11,7 +11,6 @@ package it.unibo.alchemist.boundary.launchers import io.ktor.server.engine.ApplicationEngine import io.ktor.server.engine.embeddedServer -import io.ktor.server.engine.stop import io.ktor.server.netty.Netty import it.unibo.alchemist.boundary.OutputMonitor import it.unibo.alchemist.boundary.graphql.monitor.EnvironmentSubscriptionMonitor @@ -25,7 +24,6 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.sync.Semaphore /** * An [OutputMonitor] observing the [environment] through a GraphQL server listening on [host]:[port]. diff --git a/alchemist-webgui/build.gradle.kts b/alchemist-webgui/build.gradle.kts index 0f6b29a61d..65972b7910 100644 --- a/alchemist-webgui/build.gradle.kts +++ b/alchemist-webgui/build.gradle.kts @@ -22,17 +22,17 @@ repositories { mavenLocal() } -val task by tasks.register("runWEB") { +val task by tasks.register("runWebGui") { group = alchemistGroup - description = "Launches simulation" + description = "Launches simulation in a browser web interface" val simulationFile = project.objects.property(String::class.java) - simulationFile.set(project.findProperty("simulationFile") as String? ?: "C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/Alchemist fork/Alchemist/test.yml") + simulationFile.set(project.findProperty("simulationFile") as String? ?: "") mainClass.set("it.unibo.alchemist.Alchemist") - args("run", "C:/Users/Tiziano/Desktop/Tiziano/UNI/Test/yaml/" + simulationFile.get()) + args("run", simulationFile.get()) classpath( tasks.named("compileKotlinJvm"), @@ -88,40 +88,19 @@ kotlin { val commonMain by getting { dependencies { implementation(alchemist("graphql")) + implementation(libs.kotlin.coroutines.core) implementation(libs.apollo.runtime) - implementation(libs.korim) implementation(libs.kotlin.stdlib) - implementation(libs.kotlinx.serialization.json) - implementation(libs.ktor.client.core) implementation(libs.redux.kotlin.threadsafe) } } - /*val commonTest by getting { - dependencies { - implementation(alchemist("graphql")) - //implementation(alchemist("api")) - implementation(alchemist("full")) - implementation(libs.apollo.runtime) - implementation(libs.kotlin.coroutines.core) - implementation(libs.kotlin.stdlib) - implementation(libs.kotlinx.serialization.json) - - implementation(libs.ktor.server.websockets) - implementation(libs.bundles.graphql.server) - implementation(libs.bundles.ktor.server) - - implementation(libs.kotest.assertions) - implementation(libs.kotest.runner) - implementation(libs.ktor.server.test.host) - } - }*/ - val jvmMain by getting { dependencies { implementation(incarnation("sapere")) implementation(incarnation("protelis")) implementation(alchemist("cognitive-agents")) + implementation(alchemist("full")) implementation("io.ktor:ktor-server-html-builder:2.3.8") implementation(libs.bundles.ktor.server) } @@ -130,31 +109,23 @@ kotlin { val jsMain by getting { dependencies { implementation(kotlin("stdlib-js")) + implementation(libs.kotlin.coroutines.core) + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") implementation(libs.apollo.runtime) implementation(libs.bundles.ktor.client) implementation(libs.bundles.kotlin.react) implementation("io.kvision:kvision:$kvisionVersion") implementation("io.kvision:kvision-bootstrap:$kvisionVersion") - implementation("io.kvision:kvision-datetime:$kvisionVersion") implementation("io.kvision:kvision-richtext:$kvisionVersion") implementation("io.kvision:kvision-tom-select:$kvisionVersion") implementation("io.kvision:kvision-bootstrap-upload:$kvisionVersion") - implementation("io.kvision:kvision-imask:$kvisionVersion") implementation("io.kvision:kvision-toastify:$kvisionVersion") implementation("io.kvision:kvision-fontawesome:$kvisionVersion") implementation("io.kvision:kvision-bootstrap-icons:$kvisionVersion") - implementation("io.kvision:kvision-pace:$kvisionVersion") - implementation("io.kvision:kvision-handlebars:$kvisionVersion") - implementation("io.kvision:kvision-chart:$kvisionVersion") - implementation("io.kvision:kvision-tabulator:$kvisionVersion") - implementation("io.kvision:kvision-maps:$kvisionVersion") implementation("io.kvision:kvision-rest:$kvisionVersion") - implementation("io.kvision:kvision-jquery:$kvisionVersion") - implementation("io.kvision:kvision-routing-navigo-ng:$kvisionVersion") implementation("io.kvision:kvision-state:$kvisionVersion") implementation("io.kvision:kvision-state-flow:$kvisionVersion") - implementation("io.kvision:kvision-ballast:$kvisionVersion") implementation("io.kvision:kvision-redux-kotlin:$kvisionVersion") } } diff --git a/alchemist-webgui/src/commonMain/kotlin/client/ClientConnection.kt b/alchemist-webgui/src/commonMain/kotlin/client/ClientConnection.kt index 8e6210a0d9..d68f373d31 100644 --- a/alchemist-webgui/src/commonMain/kotlin/client/ClientConnection.kt +++ b/alchemist-webgui/src/commonMain/kotlin/client/ClientConnection.kt @@ -12,7 +12,13 @@ package client import it.unibo.alchemist.boundary.graphql.client.GraphQLClient import it.unibo.alchemist.boundary.graphql.client.GraphQLClientFactory +/** + * Object representing the client connection to the GraphQL server. + */ object ClientConnection { + /** + * The GraphQL client instance used for operations. + */ val client: GraphQLClient = GraphQLClientFactory.subscriptionClient() } diff --git a/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt b/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt index e9fdb2dad5..2cd71355f7 100644 --- a/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt +++ b/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt @@ -12,6 +12,7 @@ package graphql.api import client.ClientConnection import com.apollographql.apollo3.api.ApolloResponse import com.apollographql.apollo3.api.Optional +import it.unibo.alchemist.boundary.graphql.client.EnvironmentQuery import it.unibo.alchemist.boundary.graphql.client.EnvironmentSubscription import it.unibo.alchemist.boundary.graphql.client.NodeQuery import kotlinx.coroutines.Deferred @@ -21,13 +22,35 @@ import kotlinx.coroutines.flow.Flow object EnvironmentApi { + /** + * Asynchronously queries a node with the specified ID from the server. + * + * @param nodeId the ID of the node to query (default value is 0 if not specified) + * @return a Deferred object representing the asynchronous result of the node query + */ suspend fun nodeQuery(nodeId: Int = 0): Deferred = coroutineScope { async { ClientConnection.client.query(NodeQuery(id = Optional.present(nodeId))).execute().data } } - fun environMentSubScription(): Flow> { + /** + * Subscribes to the environment subscription on the server and receives data updates. + * + * @return a Flow of ApolloResponse containing the data from the environment subscription + */ + fun environmentSubscription(): Flow> { return ClientConnection.client.subscription(EnvironmentSubscription()).toFlow() } + + /** + * Asynchronously queries environment data from the server. + * + * @return a Deferred object representing the asynchronous result of the environment query + */ + suspend fun environmentQuery(): Deferred = coroutineScope { + async { + ClientConnection.client.query(EnvironmentQuery()).execute().data + } + } } diff --git a/alchemist-webgui/src/commonMain/kotlin/graphql/api/SimulationControlApi.kt b/alchemist-webgui/src/commonMain/kotlin/graphql/api/SimulationControlApi.kt index 9c767080d0..e30de6544f 100644 --- a/alchemist-webgui/src/commonMain/kotlin/graphql/api/SimulationControlApi.kt +++ b/alchemist-webgui/src/commonMain/kotlin/graphql/api/SimulationControlApi.kt @@ -15,43 +15,42 @@ import it.unibo.alchemist.boundary.graphql.client.PlaySimulationMutation import it.unibo.alchemist.boundary.graphql.client.SimulationStatusQuery import it.unibo.alchemist.boundary.graphql.client.TerminateSimulationMutation import kotlinx.coroutines.Deferred -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.withContext object SimulationControlApi { + /** + * Asynchronously queries the status of the simulation from the server. + * + * @return a Deferred object representing the asynchronous result of the simulation status query + */ suspend fun getSimulationStatus(): Deferred = coroutineScope { async { ClientConnection.client.query(SimulationStatusQuery()).execute().data } } - suspend fun pauseSimulation() = coroutineScope { - async { - withContext(Dispatchers.Default) { - ClientConnection.client.mutation(PauseSimulationMutation()).execute() - println("COROUTINE[pauseSimulation]: Paused the simulation") - } - } + /** + * Asynchronously pauses the simulation on the server. + */ + suspend fun pauseSimulation(): PauseSimulationMutation.Data? { + return ClientConnection.client.mutation(PauseSimulationMutation()).execute().data } - suspend fun playSimulation() = coroutineScope { - async { - withContext(Dispatchers.Default) { - ClientConnection.client.mutation(PlaySimulationMutation()).execute() - println("COROUTINE[playSimulation]: Played the simulation") - } - } + /** + * Asynchronously resumes or starts the simulation on the server. + * + * @return the data object containing the result of the play simulation mutation + */ + suspend fun playSimulation(): PlaySimulationMutation.Data? { + return ClientConnection.client.mutation(PlaySimulationMutation()).execute().data } - suspend fun terminateSimulation() = coroutineScope { - async { - withContext(Dispatchers.Default) { - ClientConnection.client.mutation(TerminateSimulationMutation()).execute() - println("COROUTINE[terminateSimulation]: Terminated the simulation") - } - } + /** + * Asynchronously terminates the simulation on the server. + */ + suspend fun terminateSimulation(): TerminateSimulationMutation.Data? { + return ClientConnection.client.mutation(TerminateSimulationMutation()).execute().data } } diff --git a/alchemist-webgui/src/commonMain/kotlin/utils/SimState.kt b/alchemist-webgui/src/commonMain/kotlin/utils/SimState.kt index f5c9508a99..ca7cbe9b65 100644 --- a/alchemist-webgui/src/commonMain/kotlin/utils/SimState.kt +++ b/alchemist-webgui/src/commonMain/kotlin/utils/SimState.kt @@ -9,6 +9,10 @@ package utils +/** + * Represents the possible states of a simulation. Intermediate enum for the simulation status. + * + */ enum class SimState { /** * The simulation is being initialized. @@ -37,14 +41,21 @@ enum class SimState { TERMINATED, ; companion object { + + /** + * Converts a string representation of a simulation status to the corresponding SimState enum value. + * + * @param value the string representation of the simulation status + * @return the SimState enum value corresponding to the input string, or SimState.TERMINATED if the input is invalid + */ fun toSimStatus(value: String?): SimState { return when (value) { - "INIT" -> SimState.INIT - "READY" -> SimState.READY - "PAUSED" -> SimState.PAUSED - "RUNNING" -> SimState.RUNNING - "TERMINATED" -> SimState.TERMINATED - else -> SimState.TERMINATED // Default to a reasonable value when the string is null or unknown + "INIT" -> INIT + "READY" -> READY + "PAUSED" -> PAUSED + "RUNNING" -> RUNNING + "TERMINATED" -> TERMINATED + else -> TERMINATED } } } diff --git a/alchemist-webgui/src/jsMain/kotlin/Client.kt b/alchemist-webgui/src/jsMain/kotlin/Client.kt index 3452e2eeee..9171353cf7 100644 --- a/alchemist-webgui/src/jsMain/kotlin/Client.kt +++ b/alchemist-webgui/src/jsMain/kotlin/Client.kt @@ -26,6 +26,10 @@ import io.kvision.module import io.kvision.startApplication import ui.AppMain +/** + * The application main entry point + * + * */ fun main() { startApplication( ::AppMain, diff --git a/alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt b/alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt index 0a44b68040..11dfff10d6 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt @@ -11,46 +11,47 @@ package components import components.navbar.PlayButton import io.kvision.core.AlignItems +import io.kvision.core.Col import io.kvision.core.Color import io.kvision.core.FlexDirection -import io.kvision.form.text.text -import io.kvision.html.InputType import io.kvision.html.div import io.kvision.navbar.NavbarColor import io.kvision.navbar.NavbarType import io.kvision.navbar.nav -import io.kvision.navbar.navForm import io.kvision.navbar.navbar import io.kvision.panel.SimplePanel import io.kvision.panel.flexPanel import io.kvision.state.bind import stores.SimulationStatus -open class Appbar(className: String = "") : SimplePanel(className = className) { +/** + * Class representing the application's top navigation bar. + * + * @param className the CSS class name to be applied to the navigation bar + */ +class Appbar(className: String = "") : SimplePanel(className = className) { - private val simulationFileName = "simulationTest.yml" + private val simulationFileName = "" init { navbar( - if (simulationFileName.isEmpty()) "Alchemist" else "Alchemist - $simulationFileName", + if (simulationFileName.isEmpty()) { + "Alchemist Web Interface" + } else { + "Alchemist Web Interface - $simulationFileName" + }, collapseOnClick = false, ) { type = NavbarType.STICKYTOP nColor = NavbarColor.DARK - navForm { - text(InputType.SEARCH) { - placeholder = "Search node" - } - } - nav(rightAlign = true) { flexPanel { flexDirection = FlexDirection.ROW alignItems = AlignItems.CENTER spacing = 15 div { - color = Color("#ffffff") + color = Color.name(Col.WHITE) }.bind(SimulationStatus.simulationStore) { sim -> +"STATUS: ${sim.status?.simulationStatus}" } diff --git a/alchemist-webgui/src/jsMain/kotlin/components/Content.kt b/alchemist-webgui/src/jsMain/kotlin/components/Content.kt index 212723c4a4..a33e09b514 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/Content.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/Content.kt @@ -16,26 +16,29 @@ import io.kvision.core.AlignItems import io.kvision.core.Background import io.kvision.core.Col import io.kvision.core.Color -import io.kvision.core.FlexDirection import io.kvision.core.FlexWrap import io.kvision.core.JustifyContent import io.kvision.panel.SimplePanel -import io.kvision.panel.flexPanel import io.kvision.panel.hPanel +import io.kvision.panel.vPanel import io.kvision.utils.perc import io.kvision.utils.px -open class Content(className: String = "") : SimplePanel(className = className) { +/** + * Class representing the content panel of the application. + * This class extends the SimplePanel class and provides functionality to display simulation context, + * simulation indicators, and node properties within a structured layout. + * + * @param className the CSS class name to be applied to the content panel + */ +class Content(className: String = "") : SimplePanel(className = className) { init { - // val offcanvas = offcanvas("Lorem ipsum", OffPlacement.END, dark = true) - // offcanvas.show() - hPanel( FlexWrap.NOWRAP, JustifyContent.CENTER, - AlignItems.CENTER, + AlignItems.FLEXSTART, spacing = 5, ) { @@ -48,15 +51,14 @@ open class Content(className: String = "") : SimplePanel(className = className) }, ) - flexPanel( - FlexDirection.COLUMN, - FlexWrap.NOWRAP, + vPanel( JustifyContent.START, - AlignItems.START, + AlignItems.CENTER, spacing = 5, ) { width = 520.px height = 95.perc + marginTop = 12.px add( SimulationIndicators(className = "simulation-indicators").apply { width = 100.perc diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/CanvasHandlers.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/CanvasHandlers.kt index ff7d13cd90..ebb3b5f5a7 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/CanvasHandlers.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/CanvasHandlers.kt @@ -9,20 +9,27 @@ package components.content -import components.content.shared.CommonProperties +import components.content.shared.CommonProperties.Observables.nodesRadius +import components.content.shared.CommonProperties.Observables.scaleTranslationStore import org.w3c.dom.DOMRect import org.w3c.dom.events.MouseEvent import stores.EnvironmentStore import stores.NodeStore import utils.isMouseOverNodePosition +/** + * Finds the node corresponding to the specified mouse event position. + * + * @param event the mouse event containing the position information + * @param boundingRect the bounding rectangle of the canvas element + */ fun findNode(event: MouseEvent, boundingRect: DOMRect) { - val state = CommonProperties.Observables.scaleTranslationStore.getState() + val state = scaleTranslationStore.getState() val calcX = (event.clientX - boundingRect.left - state.translate.first) / state.scale val calcY = (event.clientY - boundingRect.top - state.translate.second) / state.scale - val radius = CommonProperties.Observables.nodesRadius.value * 1 / state.scale + val radius = nodesRadius.value * 1 / state.scale val node = EnvironmentStore.store.getState().nodes.firstOrNull { entry -> isMouseOverNodePosition(calcX, calcY, entry.position.coordinates[0], entry.position.coordinates[1], radius) diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/DrawingExtensions.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/DrawingExtensions.kt index ee4646c865..908125e86b 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/DrawingExtensions.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/DrawingExtensions.kt @@ -9,92 +9,116 @@ package components.content -import components.content.shared.CommonProperties +import components.content.shared.CommonProperties.Observables.nodesRadius +import components.content.shared.CommonProperties.Observables.scaleTranslationStore +import components.content.shared.CommonProperties.RenderProperties.DEFAULT_HEIGHT +import components.content.shared.CommonProperties.RenderProperties.DEFAULT_NODE_COLOR +import components.content.shared.CommonProperties.RenderProperties.DEFAULT_WIDTH import org.w3c.dom.CanvasRenderingContext2D -import stores.EnvironmentStore import kotlin.math.PI +/** + * Object containing properties related to grid rendering. + */ private object GridProperties { const val LINE_WIDTH = 1.0 const val STROKE_COLOR = "#ADADAD" + const val ORIGIN_COLOR = "#C22F2F" const val CELL_SIZE = 10 } +/** + * Draws a node on the canvas at the specified position with the given color. + * + * @param position the position of the node on the canvas + * @param color the color of the node + */ fun CanvasRenderingContext2D.drawNode( position: Pair, - color: String = CommonProperties.RenderProperties.DEFAULT_NODE_COLOR, + color: String = DEFAULT_NODE_COLOR, ) { + // Begin drawing a path for the node beginPath() + // Draw a circle at the specified position with radius based on the node radius and scale arc( position.first, position.second, - CommonProperties.Observables.nodesRadius.value * 1 / - CommonProperties.Observables.scaleTranslationStore.getState().scale, + nodesRadius.value * 1 / scaleTranslationStore.getState().scale, 0.0, 2 * PI, false, ) + // Set the fill style to the specified color and fill the node fillStyle = color fill() + // Close the path closePath() } +/** + * Redraws the nodes on the canvas with the specified positions. + * + * @param nodes the list of node positions + * @param scale the scale of the canvas + * @param translation the translation of the canvas + */ fun CanvasRenderingContext2D.redrawNodes( - scale: Double = CommonProperties.Observables.scaleTranslationStore.getState().scale, - translation: Pair = CommonProperties.Observables.scaleTranslationStore.getState().translate, + nodes: List>, + scale: Double = scaleTranslationStore.getState().scale, + translation: Pair = scaleTranslationStore.getState().translate, ) { - println("Function[Redraw]: Redrawing function") + // Calculate the scaled translation val translationScaled = Pair( translation.first * 1 / scale, translation.second * 1 / scale, ) + // Scale and translate the canvas scale(scale, scale) translate(translationScaled.first, translationScaled.second) + // Clear the canvas clearRect( -translationScaled.first, -translationScaled.second, - CommonProperties.RenderProperties.DEFAULT_WIDTH, - CommonProperties.RenderProperties.DEFAULT_HEIGHT, + DEFAULT_WIDTH, + DEFAULT_HEIGHT, ) + // Draw the grid drawGrid() + // Draw the origin of the canvas + drawNode(Pair(0.0, 0.0), GridProperties.ORIGIN_COLOR) - beginPath() - moveTo(0.0, 0.0) - arc( - 0.0, - 0.0, - CommonProperties.Observables.nodesRadius.value * 1 / - CommonProperties.Observables.scaleTranslationStore.getState().scale, - 0.0, - 2 * PI, - false, - ) - fillStyle = "#00ff00" - fill() - closePath() - - EnvironmentStore.store.getState().nodes.forEach { - drawNode(Pair(it.position.coordinates[0], it.position.coordinates[1])) // randomColor()) + // Draw each node at its position + nodes.forEach { + drawNode(Pair(it.first, it.second)) } + // Reset the canvas transformation setTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) } +/** + * Draws the grid lines on the canvas. + */ fun CanvasRenderingContext2D.drawGrid() { - lineWidth = GridProperties.LINE_WIDTH / CommonProperties.Observables.scaleTranslationStore.getState().scale + // Set the line width and stroke color + lineWidth = GridProperties.LINE_WIDTH / scaleTranslationStore.getState().scale strokeStyle = GridProperties.STROKE_COLOR - val left = -CommonProperties.RenderProperties.DEFAULT_WIDTH - val top = -CommonProperties.RenderProperties.DEFAULT_HEIGHT - val right = CommonProperties.RenderProperties.DEFAULT_WIDTH - val bottom = CommonProperties.RenderProperties.DEFAULT_HEIGHT + // Define the boundaries of the grid + val left = -DEFAULT_WIDTH + val top = -DEFAULT_HEIGHT + val right = DEFAULT_WIDTH + val bottom = DEFAULT_HEIGHT + // Clear the grid area clearRect(left, top, right - left, bottom - top) + // Begin drawing the grid beginPath() + // Draw vertical grid lines generateSequence(left.toInt()) { it + GridProperties.CELL_SIZE } .takeWhile { it < right.toInt() + GridProperties.CELL_SIZE } .map(Int::toDouble) @@ -103,6 +127,7 @@ fun CanvasRenderingContext2D.drawGrid() { lineTo(it, bottom) } + // Draw horizontal grid lines generateSequence(top.toInt()) { it + GridProperties.CELL_SIZE } .takeWhile { it < bottom.toInt() + GridProperties.CELL_SIZE } .map(Int::toDouble) @@ -111,6 +136,7 @@ fun CanvasRenderingContext2D.drawGrid() { lineTo(right, it) } + // Stroke the grid lines stroke() closePath() } diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/NodeProperties.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/NodeProperties.kt index b615b90419..a0ddca56ff 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/NodeProperties.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/NodeProperties.kt @@ -34,6 +34,12 @@ import io.kvision.utils.perc import io.kvision.utils.px import stores.NodeStore +/** + * Class representing the properties of a node in the application. + * This class extends SimplePanel and provides a UI component for displaying node information. + * + * @param className the CSS class name for styling the panel (optional) + */ class NodeProperties(className: String = "") : SimplePanel(className = className) { init { @@ -53,18 +59,16 @@ class NodeProperties(className: String = "") : SimplePanel(className = className width = 95.perc h5 { - +"Node evaluation" + +"Node inspection" width = 100.perc height = 100.perc } - flexPanel( - FlexDirection.ROW, + hPanel( FlexWrap.NOWRAP, JustifyContent.CENTER, - AlignItems.STRETCH, - AlignContent.CENTER, - spacing = 5, + AlignItems.START, + spacing = 15, className = "nodeinfo-header", ) { div { @@ -72,21 +76,29 @@ class NodeProperties(className: String = "") : SimplePanel(className = className height = 50.px borderRadius = CssSize(height!!.first.toDouble() * 0.5, UNIT.px) background = Background(Color(CommonProperties.RenderProperties.DEFAULT_NODE_COLOR)) - overflowWrap = OverflowWrap.ANYWHERE - } - - div { - width = 100.perc - overflowWrap = OverflowWrap.ANYWHERE - }.bind(NodeStore.nodeStore) { - +"${it.node?.environment?.nodeById?.id}" } + flexPanel( + FlexDirection.ROW, + FlexWrap.NOWRAP, + JustifyContent.CENTER, + AlignItems.STRETCH, + AlignContent.CENTER, + spacing = 5, + ) { + width = 95.perc + div { + width = 100.perc + overflowWrap = OverflowWrap.ANYWHERE + }.bind(NodeStore.nodeStore) { + +"id: ${it.node?.environment?.nodeById?.id}" + } - div { - width = 100.perc - overflowWrap = OverflowWrap.ANYWHERE - }.bind(NodeStore.nodeStore) { - +"${it.node?.nodePosition?.coordinates?.get(0)}, ${it.node?.nodePosition?.coordinates?.get(1)}" + div { + width = 100.perc + overflowWrap = OverflowWrap.ANYWHERE + }.bind(NodeStore.nodeStore) { + +"X: ${it.node?.nodePosition?.coordinates?.get(0)}, Y:${it.node?.nodePosition?.coordinates?.get(1)}" + } } } @@ -115,12 +127,12 @@ class NodeProperties(className: String = "") : SimplePanel(className = className div { width = 50.perc overflowWrap = OverflowWrap.ANYWHERE - +it.molecule.name + +"Name: ${it.molecule.name}" } div { width = 50.perc overflowWrap = OverflowWrap.ANYWHERE - +it.concentration + +"Concentration: ${it.concentration}" } } } diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt index 6f2cd12337..21a1575bab 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt @@ -9,7 +9,10 @@ package components.content -import components.content.shared.CommonProperties +import components.content.shared.CommonProperties.Observables.nodesRadius +import components.content.shared.CommonProperties.Observables.scaleTranslationStore +import components.content.shared.CommonProperties.RenderProperties +import components.content.shared.CommonProperties.Utils.nextScale import io.kvision.core.AlignItems import io.kvision.core.Background import io.kvision.core.BoxShadow @@ -29,24 +32,39 @@ import io.kvision.panel.flexPanel import io.kvision.state.bind import io.kvision.utils.perc import io.kvision.utils.px +import org.w3c.dom.CanvasRenderingContext2D import org.w3c.dom.DOMRect import stores.EnvironmentStore import stores.actions.ScaleTranslateAction +/** + * Class representing the simulation context in the application. + * This class extends SimplePanel and provides a UI component for rendering the simulation environment. + * + * @param className the CSS class name for styling the panel (optional) + */ class SimulationContext(className: String = "") : SimplePanel(className = className) { + private lateinit var canvasCtxt: CanvasRenderingContext2D + private var translatePos: Pair = Pair(0.0, 0.0) private var startDragOffset: Pair = Pair( - CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble(), - CommonProperties.RenderProperties.DEFAULT_START_POSITION.toDouble(), + RenderProperties.DEFAULT_START_POSITION.toDouble(), + RenderProperties.DEFAULT_START_POSITION.toDouble(), ) init { - // border = Border(width = 1.px, BorderStyle.SOLID, Color("#ff0000")) - EnvironmentStore.callEnvironmentSubscription() + EnvironmentStore.callEnvironmentQuery() + + // Workaround for node re-render as bind function seems to reload the whole object in the DOM + EnvironmentStore.store.subscribe { state -> + if (::canvasCtxt.isInitialized) { + canvasCtxt.redrawNodes(state.toListOfPairs()) + } + } flexPanel( FlexDirection.COLUMN, @@ -61,30 +79,33 @@ class SimulationContext(className: String = "") : SimplePanel(className = classN lateinit var boundingRect: DOMRect var mouseIsDown = false - canvasWidth = CommonProperties.RenderProperties.DEFAULT_WIDTH.toInt() - canvasHeight = CommonProperties.RenderProperties.DEFAULT_HEIGHT.toInt() + canvasWidth = RenderProperties.DEFAULT_WIDTH.toInt() + canvasHeight = RenderProperties.DEFAULT_HEIGHT.toInt() borderRadius = CssSize(10, UNIT.px) boxShadow = BoxShadow(0.px, 0.px, 5.px, 0.px, Color.rgba(0, 0, 0, (0.4 * 255).toInt())) background = Background(color = Color.name(Col.WHITE)) - addAfterCreateHook { + addAfterInsertHook { + boundingRect = getElement()!!.getBoundingClientRect() + canvasCtxt = context2D - bind(CommonProperties.Observables.scaleTranslationStore) { state -> - context2D.redrawNodes(state.scale, state.translate) - } + // Sets the canvas to a centered position + translatePos = Pair(RenderProperties.DEFAULT_WIDTH / 2, RenderProperties.DEFAULT_HEIGHT / 2) + scaleTranslationStore.dispatch(ScaleTranslateAction.SetTranslation(translatePos)) - bind(CommonProperties.Observables.nodesRadius) { - context2D.redrawNodes() - } + context2D.redrawNodes(EnvironmentStore.store.getState().toListOfPairs()) - bind(EnvironmentStore.store) { - println("Bind[EnvironmentStore.store]: Redrawing nodes on store bind") - context2D.redrawNodes() + bind(scaleTranslationStore) { state -> + context2D.redrawNodes( + EnvironmentStore.store.getState().toListOfPairs(), + state.scale, + state.translate, + ) } - } - addAfterInsertHook { - boundingRect = getElement()!!.getBoundingClientRect() + bind(nodesRadius) { + context2D.redrawNodes(EnvironmentStore.store.getState().toListOfPairs()) + } } onClick { @@ -94,11 +115,10 @@ class SimulationContext(className: String = "") : SimplePanel(className = classN onEvent { mousedown = { e -> - cursor = Cursor.POINTER mouseIsDown = true startDragOffset = Pair( - e.clientX - CommonProperties.Observables.scaleTranslationStore.getState().translate.first, - e.clientY - CommonProperties.Observables.scaleTranslationStore.getState().translate.second, + e.clientX - scaleTranslationStore.getState().translate.first, + e.clientY - scaleTranslationStore.getState().translate.second, ) } @@ -107,7 +127,7 @@ class SimulationContext(className: String = "") : SimplePanel(className = classN cursor = Cursor.GRABBING translatePos = Pair(e.clientX - startDragOffset.first, e.clientY - startDragOffset.second) - CommonProperties.Observables.scaleTranslationStore.dispatch( + scaleTranslationStore.dispatch( ScaleTranslateAction.SetTranslation(translatePos), ) } @@ -120,12 +140,12 @@ class SimulationContext(className: String = "") : SimplePanel(className = classN mousewheel = { e -> e.preventDefault() - val nextScale = CommonProperties.Utils.nextScale(e.deltaY) + val nextScale = nextScale(e.deltaY) - if (nextScale <= CommonProperties.RenderProperties.MAX_SCALE && - nextScale >= CommonProperties.RenderProperties.MIN_SCALE + if (nextScale <= RenderProperties.MAX_SCALE && + nextScale >= RenderProperties.MIN_SCALE ) { - val currentState = CommonProperties.Observables.scaleTranslationStore.getState() + val currentState = scaleTranslationStore.getState() val scaleChangeFactor = nextScale / currentState.scale val translationChangeX = @@ -136,11 +156,11 @@ class SimulationContext(className: String = "") : SimplePanel(className = classN translatePos = Pair(translationChangeX, translationChangeY) - CommonProperties.Observables.scaleTranslationStore.dispatch( + scaleTranslationStore.dispatch( ScaleTranslateAction.SetScale(nextScale), ) - CommonProperties.Observables.scaleTranslationStore.dispatch( + scaleTranslationStore.dispatch( ScaleTranslateAction.SetTranslation( Pair( currentState.translate.first + translationChangeX, diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt index af539ba76e..61d861d911 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt @@ -9,7 +9,11 @@ package components.content -import components.content.shared.CommonProperties +import components.content.shared.CommonProperties.Observables.nodesRadius +import components.content.shared.CommonProperties.Observables.scaleTranslationStore +import components.content.shared.CommonProperties.RenderProperties.DEFUALT_NODE_RADIUS +import components.content.shared.CommonProperties.RenderProperties.MAX_SCALE +import components.content.shared.CommonProperties.RenderProperties.MIN_SCALE import io.kvision.core.AlignItems import io.kvision.core.Background import io.kvision.core.BoxShadow @@ -21,6 +25,7 @@ import io.kvision.core.JustifyContent import io.kvision.core.onInput import io.kvision.form.number.range import io.kvision.html.div +import io.kvision.html.h5 import io.kvision.html.p import io.kvision.panel.SimplePanel import io.kvision.panel.flexPanel @@ -32,6 +37,12 @@ import io.kvision.utils.perc import io.kvision.utils.px import korlibs.math.roundDecimalPlaces +/** + * Class representing simulation indicators in the application. + * This class extends SimplePanel and provides a UI component for displaying simulation context information. + * + * @param className the CSS class name for styling the panel (optional) + */ class SimulationIndicators(className: String = "") : SimplePanel(className = className) { init { @@ -49,11 +60,17 @@ class SimulationIndicators(className: String = "") : SimplePanel(className = cla spacing = 5 width = 95.perc - div().bind(CommonProperties.Observables.scaleTranslationStore) { state -> + h5 { + +"Context information" + width = 100.perc + height = 100.perc + } + + div().bind(scaleTranslationStore) { state -> +"Scale: ${state.scale.roundDecimalPlaces(2)}" } - div().bind(CommonProperties.Observables.scaleTranslationStore) { state -> + div().bind(scaleTranslationStore) { state -> div { +"X translation: ${state.translate.first.roundDecimalPlaces(2)}" @@ -66,33 +83,29 @@ class SimulationIndicators(className: String = "") : SimplePanel(className = cla range { label = "Node radius: $min ($min - $max)" - min = CommonProperties.RenderProperties.DEFUALT_NODE_RADIUS - max = CommonProperties.RenderProperties.DEFUALT_NODE_RADIUS * 10 + min = DEFUALT_NODE_RADIUS + max = DEFUALT_NODE_RADIUS * 10 step = 1.0 - value = CommonProperties.RenderProperties.DEFUALT_NODE_RADIUS + value = DEFUALT_NODE_RADIUS onInput { - CommonProperties.Observables.nodesRadius.value = getValue()!!.toDouble() + nodesRadius.value = getValue()!!.toDouble() } - }.bind(CommonProperties.Observables.nodesRadius) { - label = "Node radius: ${CommonProperties.Observables.nodesRadius.value} ($min - $max)" + }.bind(nodesRadius) { + label = "Node radius: ${nodesRadius.value} ($min - $max)" } - /*div().bind(CommonProperties.Observables.nodesRadius) { - +"Node radius: ${CommonProperties.Observables.nodesRadius.value}" - }*/ - - p(className = "scale-value").bind(CommonProperties.Observables.scaleTranslationStore) { state -> - +"Zoom: ${((state.scale/CommonProperties.RenderProperties.MAX_SCALE) * 100).roundDecimalPlaces(0)}%" + p(className = "scale-value").bind(scaleTranslationStore) { state -> + +"Zoom: ${((state.scale/MAX_SCALE) * 100).roundDecimalPlaces(0)}%" } progress( - min = CommonProperties.RenderProperties.MIN_SCALE, - max = CommonProperties.RenderProperties.MAX_SCALE, + min = MIN_SCALE, + max = MAX_SCALE, ) { progressNumeric { striped = false } - }.getFirstProgressBar()!!.bind(CommonProperties.Observables.scaleTranslationStore) { state -> + }.getFirstProgressBar()!!.bind(scaleTranslationStore) { state -> value = state.scale } } diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/common/Collapsible.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/common/Collapsible.kt index c294caef84..09af35a54a 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/common/Collapsible.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/common/Collapsible.kt @@ -9,6 +9,13 @@ import io.kvision.panel.simplePanel import io.kvision.panel.vPanel import io.kvision.utils.perc +/** + * DSL extension function that creates a collapsible container with a title and content panel. + * + * @param className the CSS class name for styling the container + * @param title the title of the collapsible container + * @param content a lambda function representing the content of the collapsible container + */ fun Container.collapsible( className: String = "", title: String = "", diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt index e4b99453e2..bdcfd77d33 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt @@ -13,10 +13,12 @@ import io.kvision.redux.createTypedReduxStore import io.kvision.state.ObservableValue import stores.reducers.scaleTranslateReducer import stores.states.ScaleTranslateState -import utils.randomColor class CommonProperties { + /** + * Object containing properties related to the canvas context. + */ object RenderProperties { private const val SCALE_ITERATIONS = 100 const val DEFUALT_NODE_RADIUS = 10.0 @@ -26,16 +28,26 @@ class CommonProperties { const val DEFAULT_START_POSITION = 0 const val DEFAULT_SCALE_RATIO = 0.9 const val MIN_SCALE = 1.0 - const val MAX_SCALE = MIN_SCALE * (1 / DEFAULT_SCALE_RATIO) * SCALE_ITERATIONS - val DEFAULT_NODE_COLOR = randomColor() + const val MAX_SCALE = MIN_SCALE * ((1 / DEFAULT_SCALE_RATIO) * SCALE_ITERATIONS + 1) + const val DEFAULT_NODE_COLOR = "#0F4AA2" } + /** + * Object containing observable properties. + */ object Observables { val scaleTranslationStore = createTypedReduxStore(::scaleTranslateReducer, ScaleTranslateState()) var nodesRadius: ObservableValue = ObservableValue(RenderProperties.DEFUALT_NODE_RADIUS) } object Utils { + + /** + * Calculates the next scale value based on the specified threshold. + * + * @param threshold the threshold value used to determine the direction of scaling + * @return the next scale value calculated based on the threshold + */ fun nextScale(threshold: Double): Double { return if (threshold > 0) { Observables.scaleTranslationStore.getState().scale * RenderProperties.DEFAULT_SCALE_RATIO diff --git a/alchemist-webgui/src/jsMain/kotlin/components/navbar/PlayButton.kt b/alchemist-webgui/src/jsMain/kotlin/components/navbar/PlayButton.kt index 2d23fdcfba..29a4782494 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/navbar/PlayButton.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/navbar/PlayButton.kt @@ -14,11 +14,19 @@ import io.kvision.html.Button import io.kvision.html.ButtonStyle import io.kvision.html.ButtonType import io.kvision.state.bind -import kotlinx.coroutines.MainScope +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import stores.EnvironmentStore import stores.SimulationStatus import utils.SimState +/** + * Class representing a custom play button for controlling simulation state. + * This class extends the Button class and provides functionality to play, pause, or terminate a simulation. + * + * @param text the text to be displayed on the button + */ class PlayButton(text: String) : Button(text) { private var simulationStatus = SimState.TERMINATED @@ -54,22 +62,23 @@ class PlayButton(text: String) : Button(text) { } onClick { - MainScope().launch { - println("Button clicked coroutine") - + CoroutineScope(Dispatchers.Default).launch { when (simulationStatus) { SimState.READY -> { SimulationControlApi.playSimulation() + EnvironmentStore.callEnvironmentSubscription() } SimState.PAUSED -> { - SimulationControlApi.playSimulation() + EnvironmentStore.callEnvironmentSubscription() } SimState.RUNNING -> { SimulationControlApi.pauseSimulation() + // EnvironmentStore.cancelSubscription() } else -> { SimulationControlApi.terminateSimulation() + // EnvironmentStore.cancelSubscription() } } SimulationStatus.callGetStatus() diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt b/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt index ddf8dfdaa0..28a46a35f2 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt @@ -11,6 +11,8 @@ package stores import graphql.api.EnvironmentApi import io.kvision.redux.createTypedReduxStore +import it.unibo.alchemist.boundary.graphql.client.EnvironmentSubscription +import korlibs.io.async.async import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -18,17 +20,52 @@ import stores.actions.EnvironmentStateAction import stores.reducers.environmentReducer import stores.states.EnvironmentState +/** + * Provides access to the Redux store for the environment state. + */ object EnvironmentStore { + /** + * The Redux store for managing environment state, initialized with a default reducer and initial state. + */ val store = createTypedReduxStore(::environmentReducer, EnvironmentState(mutableListOf())) + // private lateinit var job: Job - fun callEnvironmentSubscription() { - CoroutineScope(Dispatchers.Default).launch { - println("COROUTINE[callEnvironmentSubscription]: Environmnent subscription started") - EnvironmentApi.environMentSubScription().collect { + /** + * Asynchronously calls the environment subscription API and dispatches the received data to update the Redux state. + * + * This function launches a coroutine on the default dispatcher to perform the asynchronous operation. + * The environment subscription data is collected from the API using a Flow, and the collected data is then + * dispatched to update the Redux state by adding all nodes. + */ + suspend fun callEnvironmentSubscription() { + async(Dispatchers.Default) { + EnvironmentApi.environmentSubscription().collect { store.dispatch(EnvironmentStateAction.AddAllNodes(it.data?.environment?.nodeToPos!!.entries)) } - println("COROUTINE[callEnvironmentSubscription]: Environmnent subscription ended") } } + + /** + * Calls the environment query to fetch node positions asynchronously and dispatches + * the result to the store. + */ + + // This could be avoided by generalizing the store type + fun callEnvironmentQuery() { + CoroutineScope(Dispatchers.Default).launch { + val result = EnvironmentApi.environmentQuery().await() + ?.environment + ?.nodeToPos + ?.entries!! + .map { e -> EnvironmentSubscription.Entry(e.id, EnvironmentSubscription.Position(e.position.coordinates)) } + store.dispatch(EnvironmentStateAction.AddAllNodes(result)) + } + } + + // It seems to degrade performance. Needs further analysis... + /*fun cancelSubscription(){ + if(::job.isInitialized) + job.cancel() + }*/ } diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/NodeStore.kt b/alchemist-webgui/src/jsMain/kotlin/stores/NodeStore.kt index 1dacf795a1..f35a479dec 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/NodeStore.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/NodeStore.kt @@ -17,17 +17,26 @@ import stores.actions.NodeStateAction import stores.reducers.nodeReducer import stores.states.NodeState +/** + * Provides access to the Redux store for the selected node state. + */ object NodeStore { + /** + * The Redux store for managing node state, initialized with a default reducer and initial state. + */ val nodeStore = createTypedReduxStore(::nodeReducer, NodeState()) + /** + * Retrieves node data by ID and updates the node state in the store asynchronously. + * @param nodeId The ID of the node to retrieve. Defaults to 0 if not specified. + */ fun nodeById(nodeId: Int = 0) { MainScope().launch { - println("COROUTINE[nodeById]: Started") val result = EnvironmentApi.nodeQuery(nodeId).await() + // Dispatches an action to update the node state in the store with the retrieved data. nodeStore.dispatch(NodeStateAction.SetNode(result)) - println("COROUTINE[nodeById]: Ended") } } } diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/SimulationStatus.kt b/alchemist-webgui/src/jsMain/kotlin/stores/SimulationStatus.kt index c696767c89..59fc2bec32 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/SimulationStatus.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/SimulationStatus.kt @@ -17,17 +17,25 @@ import stores.actions.SimulationAction import stores.reducers.simulationReducer import stores.states.SimulationState +/** + * Provides access to the Redux store for simulation status-related state management. + */ object SimulationStatus { + /** + * The Redux store for managing simulation status state, initialized with a default reducer and initial state. + */ val simulationStore = createTypedReduxStore(::simulationReducer, SimulationState()) + /** + * Asynchronously retrieves the simulation status and updates the simulation state in the store. + * Dispatches an action to update the simulation state in the store with the retrieved status data. + */ fun callGetStatus() { MainScope().launch { - println("COROUTINE[CALLGETSTATUS]: Started") val result = SimulationControlApi.getSimulationStatus().await() simulationStore.dispatch(SimulationAction.SetSimulation(result)) - println("COROUTINE[CALLGETSTATUS]: Ended") } } } diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/actions/EnvironmentStateAction.kt b/alchemist-webgui/src/jsMain/kotlin/stores/actions/EnvironmentStateAction.kt index fe2e2a5ece..e382b46cd3 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/actions/EnvironmentStateAction.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/actions/EnvironmentStateAction.kt @@ -12,7 +12,21 @@ package stores.actions import io.kvision.redux.RAction import it.unibo.alchemist.boundary.graphql.client.EnvironmentSubscription +/** + * Represents actions that can be performed on the environment state. + * This sealed class defines different types of actions as its subclasses. + */ sealed class EnvironmentStateAction : RAction { + + /** + * Action to set the nodes in the environment state. + * @param nodes The list of nodes to set. + */ data class SetNodes(val nodes: MutableList) : EnvironmentStateAction() + + /** + * Action to add all nodes to the environment state. + * @param nodes The list of nodes to add. + */ data class AddAllNodes(val nodes: List) : EnvironmentStateAction() } diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/actions/NodeStateAction.kt b/alchemist-webgui/src/jsMain/kotlin/stores/actions/NodeStateAction.kt index 9bb0bc9dcc..ef6ce6af55 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/actions/NodeStateAction.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/actions/NodeStateAction.kt @@ -12,6 +12,15 @@ package stores.actions import io.kvision.redux.RAction import it.unibo.alchemist.boundary.graphql.client.NodeQuery +/** + * Represents actions that can be performed on the node state. + * This sealed class defines different types of actions as its subclasses. + */ sealed class NodeStateAction : RAction { + + /** + * Action to set a single node in the node state. + * @param node The node data to set. + */ data class SetNode(val node: NodeQuery.Data?) : NodeStateAction() } diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/actions/ScaleTranslateAction.kt b/alchemist-webgui/src/jsMain/kotlin/stores/actions/ScaleTranslateAction.kt index eb2c0ce108..c98b3f3bd4 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/actions/ScaleTranslateAction.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/actions/ScaleTranslateAction.kt @@ -11,7 +11,21 @@ package stores.actions import io.kvision.redux.RAction +/** + * Represents actions related to scaling and translation in the application. + * This sealed class defines different types of actions as its subclasses. + */ sealed class ScaleTranslateAction : RAction { + + /** + * Action to set the scale value. + * @param scale The new scale value to set. + */ data class SetScale(val scale: Double) : ScaleTranslateAction() + + /** + * Action to set the translation values. + * @param translate The new translation pair to set, representing the (x, y) translation. + */ data class SetTranslation(var translate: Pair) : ScaleTranslateAction() } diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/actions/SimulationAction.kt b/alchemist-webgui/src/jsMain/kotlin/stores/actions/SimulationAction.kt index 5d800401b2..aae8388cbc 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/actions/SimulationAction.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/actions/SimulationAction.kt @@ -12,6 +12,15 @@ package stores.actions import io.kvision.redux.RAction import it.unibo.alchemist.boundary.graphql.client.SimulationStatusQuery +/** + * Represents actions related to simulation in the application. + * This sealed class defines different types of actions as its subclasses. + */ sealed class SimulationAction : RAction { + + /** + * Action to set the simulation state. + * @param simulationState The new simulation state to set. + */ data class SetSimulation(val simulationState: SimulationStatusQuery.Data?) : SimulationAction() } diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/reducers/EnvironmentReducer.kt b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/EnvironmentReducer.kt index 87e5c151ce..f6a8f8be57 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/reducers/EnvironmentReducer.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/EnvironmentReducer.kt @@ -12,13 +12,19 @@ package stores.reducers import stores.actions.EnvironmentStateAction import stores.states.EnvironmentState +/** + * Reduces the current state of the environment based on the provided action. + * @param state The current state of the environment. + * @param action The action to apply to the state. + * @return The new state of the environment after applying the action. + */ fun environmentReducer(state: EnvironmentState, action: EnvironmentStateAction): EnvironmentState { when (action) { is EnvironmentStateAction.SetNodes -> { return state.copy(nodes = action.nodes) } - is EnvironmentStateAction.AddAllNodes -> { + // Clear the existing nodes and add all nodes from the action state.nodes.clear() state.nodes.addAll(action.nodes) return state diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/reducers/NodeReducer.kt b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/NodeReducer.kt index 9e3bd52362..a91202df3b 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/reducers/NodeReducer.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/NodeReducer.kt @@ -12,6 +12,12 @@ package stores.reducers import stores.actions.NodeStateAction import stores.states.NodeState +/** + * Reduces the current state of the node based on the provided action. + * @param state The current state of the node. + * @param action The action to apply to the state. + * @return The new state of the node after applying the action. + */ fun nodeReducer(state: NodeState, action: NodeStateAction): NodeState { when (action) { is NodeStateAction.SetNode -> { diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/reducers/ScaleTranslateReducer.kt b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/ScaleTranslateReducer.kt index 952c2bbedf..e7d713ebfe 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/reducers/ScaleTranslateReducer.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/ScaleTranslateReducer.kt @@ -12,6 +12,12 @@ package stores.reducers import stores.actions.ScaleTranslateAction import stores.states.ScaleTranslateState +/** + * Reduces the current state of scale and translation based on the provided action. + * @param state The current state of scale and translation. + * @param action The action to apply to the state. + * @return The new state of scale and translation after applying the action. + */ fun scaleTranslateReducer(state: ScaleTranslateState, action: ScaleTranslateAction): ScaleTranslateState = when (action) { is ScaleTranslateAction.SetScale -> { state.copy(scale = action.scale) diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/reducers/SimulationReducer.kt b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/SimulationReducer.kt index 72c70b4369..bcda55a30e 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/reducers/SimulationReducer.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/SimulationReducer.kt @@ -12,6 +12,12 @@ package stores.reducers import stores.actions.SimulationAction import stores.states.SimulationState +/** + * Reduces the current state of the simulation based on the provided action. + * @param state The current state of the simulation. + * @param action The action to apply to the state. + * @return The new state of the simulation after applying the action. + */ fun simulationReducer(state: SimulationState, action: SimulationAction): SimulationState = when (action) { is SimulationAction.SetSimulation -> { state.copy(status = action.simulationState) diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/states/EnvironmentState.kt b/alchemist-webgui/src/jsMain/kotlin/stores/states/EnvironmentState.kt index 3b9c8f70b7..fcf7ebd8aa 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/states/EnvironmentState.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/states/EnvironmentState.kt @@ -11,4 +11,18 @@ package stores.states import it.unibo.alchemist.boundary.graphql.client.EnvironmentSubscription -data class EnvironmentState(val nodes: MutableList) +/** + * Represents the state of the environment, including a list of nodes. + * @property nodes The list of nodes in the environment. + * @constructor Creates an EnvironmentState with the specified list of nodes. + */ +data class EnvironmentState(val nodes: MutableList) { + + /** + * Converts the list of nodes to a list of pairs representing their coordinates. + * @return A list of pairs where each pair contains the x and y coordinates of a node. + */ + fun toListOfPairs(): List> { + return nodes.map { e -> Pair(e.position.coordinates[0], e.position.coordinates[1]) } + } +} diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/states/NodeState.kt b/alchemist-webgui/src/jsMain/kotlin/stores/states/NodeState.kt index 91a076d572..6551e70b19 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/states/NodeState.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/states/NodeState.kt @@ -11,4 +11,9 @@ package stores.states import it.unibo.alchemist.boundary.graphql.client.NodeQuery +/** + * Represents the state of a node, including its data retrieved from a NodeQuery. + * @property node The node data retrieved from a NodeQuery, or null if the node is not available. + * @constructor Creates a NodeState with the specified node data, which defaults to null. + */ data class NodeState(val node: NodeQuery.Data? = null) diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/states/ScaleTranslateState.kt b/alchemist-webgui/src/jsMain/kotlin/stores/states/ScaleTranslateState.kt index 8f45147902..fe90a7f7ca 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/states/ScaleTranslateState.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/states/ScaleTranslateState.kt @@ -11,6 +11,12 @@ package stores.states import components.content.shared.CommonProperties.RenderProperties.DEFAULT_SCALE +/** + * Represents the state of scaling and translation in the canvas context. + * @property scale The scaling factor applied to the graphical elements. Defaults to DEFAULT_SCALE. + * @property translate The translation offset applied to the graphical elements, represented as a pair of (x, y) coordinates. Defaults to (0.0, 0.0). + * @constructor Creates a ScaleTranslateState with the specified scaling factor and translation offset, which default to DEFAULT_SCALE and (0.0, 0.0) respectively. + */ data class ScaleTranslateState( var scale: Double = DEFAULT_SCALE, var translate: Pair = Pair(0.0, 0.0), diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/states/SimulationState.kt b/alchemist-webgui/src/jsMain/kotlin/stores/states/SimulationState.kt index 1b1b5aa637..e8561fff0b 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/states/SimulationState.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/states/SimulationState.kt @@ -11,4 +11,9 @@ package stores.states import it.unibo.alchemist.boundary.graphql.client.SimulationStatusQuery +/** + * Represents the state of a simulation, including its status data. + * @property status The status data of the simulation, represented by a SimulationStatusQuery.Data object. Defaults to null. + * @constructor Creates a SimulationState with the specified status data, which defaults to null. + */ data class SimulationState(val status: SimulationStatusQuery.Data? = null) diff --git a/alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt b/alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt index 8d144ba41c..77a2fcc9ff 100644 --- a/alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt +++ b/alchemist-webgui/src/jsMain/kotlin/ui/AppMain.kt @@ -14,7 +14,16 @@ import components.Content import io.kvision.Application import io.kvision.panel.root +/** + * Represents the main entry point of the application. + * Extends the Application class to define the application lifecycle. + */ class AppMain : Application() { + + /** + * Override of the start method to initialize and configure the application. + * Sets up the root UI element and adds the Appbar and Content components to it. + */ override fun start() { root(id = "root") { add(Appbar(className = "appbar-root")) diff --git a/alchemist-webgui/src/jsMain/kotlin/utils/RenderingUtils.kt b/alchemist-webgui/src/jsMain/kotlin/utils/RenderingUtils.kt index 1db7839a16..265525f383 100644 --- a/alchemist-webgui/src/jsMain/kotlin/utils/RenderingUtils.kt +++ b/alchemist-webgui/src/jsMain/kotlin/utils/RenderingUtils.kt @@ -11,6 +11,16 @@ package utils import kotlin.random.Random +/** + * Checks if the mouse pointer is over a node position within the given radius. + * + * @param mouseX The x-coordinate of the mouse pointer. + * @param mouseY The y-coordinate of the mouse pointer. + * @param nodeX The x-coordinate of the node position. + * @param nodeY The y-coordinate of the node position. + * @param radius The radius of the node. + * @return True if the mouse pointer is over the node position, false otherwise. + */ fun isMouseOverNodePosition(mouseX: Double, mouseY: Double, nodeX: Double, nodeY: Double, radius: Double): Boolean { val dx = mouseX - nodeX val dy = mouseY - nodeY @@ -18,6 +28,11 @@ fun isMouseOverNodePosition(mouseX: Double, mouseY: Double, nodeX: Double, nodeY return dx * dx + dy * dy <= radius * radius } +/** + * Generates a random hexadecimal color string. + * + * @return A random hexadecimal color string. + */ fun randomColor(): String { val letters = "0123456789ABCDEF" var color = "#" diff --git a/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/server/modules/InstallModule.kt b/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/server/modules/InstallModule.kt index 4ba400d04b..d34aa0424f 100644 --- a/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/server/modules/InstallModule.kt +++ b/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/server/modules/InstallModule.kt @@ -14,18 +14,16 @@ import io.ktor.server.application.Application import io.ktor.server.application.install import io.ktor.server.plugins.cors.routing.CORS +/** + * Installs the CORS module for the application. + * + */ fun Application.installModule() { -/*install(ContentNegotiation) { - json() - }*/ - install(CORS) { + allowSameOrigin + allowCredentials = true allowMethod(HttpMethod.Get) allowMethod(HttpMethod.Post) anyHost() } - -/*install(Compression) { - gzip() - }*/ } diff --git a/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/server/modules/RoutingModule.kt b/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/server/modules/RoutingModule.kt index 0d0930517f..e24bdbb45e 100644 --- a/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/server/modules/RoutingModule.kt +++ b/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/server/modules/RoutingModule.kt @@ -9,12 +9,29 @@ package it.unibo.alchemist.boundary.server.modules +import com.google.common.io.Resources +import io.ktor.http.ContentType import io.ktor.server.application.Application +import io.ktor.server.application.call +import io.ktor.server.http.content.staticResources +import io.ktor.server.response.respondText +import io.ktor.server.routing.get import io.ktor.server.routing.routing -import it.unibo.alchemist.boundary.server.routes.mainRoute +/** + * Configures the routing for the application. + * This function sets up routes to handle incoming requests and serve static resources. + * + * - GET requests to the root path ("/") are handled by responding with the contents of the "index.html" file. + * - Static resources are served from the root path ("/"). + */ fun Application.routingModule() { routing { - mainRoute() + get("/") { + val resource = Resources.getResource("index.html") + call.respondText(resource.readText(), ContentType.Text.Html) + } + + staticResources("/", "") } } diff --git a/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/server/routes/MainRoute.kt b/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/server/routes/MainRoute.kt deleted file mode 100644 index 0d8a9a71b3..0000000000 --- a/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/server/routes/MainRoute.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2010-2024, Danilo Pianini and contributors - * listed, for each module, in the respective subproject's build.gradle.kts file. - * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. - */ - -package it.unibo.alchemist.boundary.server.routes - -import com.google.common.io.Resources -import com.google.common.io.Resources.getResource -import io.ktor.http.ContentType -import io.ktor.server.application.call -import io.ktor.server.http.content.staticResources -import io.ktor.server.response.respondText -import io.ktor.server.routing.Route -import io.ktor.server.routing.get - -fun Route.mainRoute() { - get("/") { - val resource = Resources.getResource("index.html") - call.respondText(resource.readText(), ContentType.Text.Html) - } - - staticResources("/", "") -} diff --git a/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/webgui/monitor/WebUIMonitor.kt b/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/webgui/monitor/WebUIMonitor.kt index 024d5c3c10..05b3d93598 100644 --- a/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/webgui/monitor/WebUIMonitor.kt +++ b/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/webgui/monitor/WebUIMonitor.kt @@ -9,64 +9,56 @@ package it.unibo.alchemist.boundary.webgui.monitor -import com.google.common.io.Resources -import io.ktor.http.ContentType -import io.ktor.http.HttpMethod -import io.ktor.server.application.call -import io.ktor.server.application.install +import io.ktor.server.engine.ApplicationEngine import io.ktor.server.engine.embeddedServer -import io.ktor.server.http.content.staticResources import io.ktor.server.netty.Netty -import io.ktor.server.plugins.cors.routing.CORS -import io.ktor.server.response.respondText -import io.ktor.server.routing.get -import io.ktor.server.routing.routing +import it.unibo.alchemist.boundary.server.modules.installModule +import it.unibo.alchemist.boundary.server.modules.routingModule import it.unibo.alchemist.model.Environment import it.unibo.alchemist.model.Position +import it.unibo.alchemist.model.Time import korlibs.io.async.launch import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import it.unibo.alchemist.boundary.launchers.GraphQLServer as GraphQLServer -/* - * Copyright (C) 2010-2024, Danilo Pianini and contributors - * listed, for each module, in the respective subproject's build.gradle.kts file. +/** + * Represents a GraphQL server with a web-based user interface monitor. + * This class extends the GraphQLServer class and provides functionality to start and stop a web server + * client-side alongside the GraphQL server. * - * This file is part of Alchemist, and is distributed under the terms of the - * GNU General Public License, with a linking exception, - * as described in the file LICENSE in the Alchemist distribution's top directory. + * @param T The type of elements in the environment. + * @param P The type of positions associated with elements in the environment. + * @param environment The environment to monitor using the web UI. */ - class WebUIMonitor> ( environment: Environment, ) : GraphQLServer(environment) { private val serverDispatcher: CoroutineDispatcher = Dispatchers.Default + private lateinit var webServer: ApplicationEngine override fun initialized(environment: Environment) { super.initialized(environment) - startWebServer() - } - - private fun startWebServer() { + webServer = startWebServer() launch(serverDispatcher) { - embeddedServer(Netty, port = 9090) { - install(CORS) { - allowSameOrigin - allowCredentials = true - allowMethod(HttpMethod.Get) - allowMethod(HttpMethod.Post) - anyHost() - } - - routing { - get("/") { - val resource = Resources.getResource("index.html") - call.respondText(resource.readText(), ContentType.Text.Html) - } + webServer.start(wait = false) + } + } - staticResources("/", "") - } - }.start(wait = false) + override fun finished(environment: Environment, time: Time, step: Long) { + this.finished(environment, time, step) + if (teardownOnSimulationTermination) { + webServer.stop() } } + + private fun startWebServer(): ApplicationEngine = + embeddedServer( + Netty, + port = 9090, + module = { + installModule() + routingModule() + }, + ) } From f47aff8f0fe2b54f648c694d9b9e11a4c21a9f36 Mon Sep 17 00:00:00 2001 From: Tiziano Vuksan Date: Thu, 15 Feb 2024 21:00:21 +0100 Subject: [PATCH 11/22] ci(webgui): fixed missing imports --- alchemist-webgui/.kotlin-js-store/yarn.lock | 5 ----- alchemist-webgui/build.gradle.kts | 1 + alchemist-webgui/src/jsMain/kotlin/Client.kt | 14 -------------- 3 files changed, 1 insertion(+), 19 deletions(-) diff --git a/alchemist-webgui/.kotlin-js-store/yarn.lock b/alchemist-webgui/.kotlin-js-store/yarn.lock index a358b793c5..165b2fab8b 100644 --- a/alchemist-webgui/.kotlin-js-store/yarn.lock +++ b/alchemist-webgui/.kotlin-js-store/yarn.lock @@ -2595,11 +2595,6 @@ nanoid@^3.3.7: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== -navigo@^8.11.1: - version "8.11.1" - resolved "https://registry.yarnpkg.com/navigo/-/navigo-8.11.1.tgz#aca756dbca40e2da2ae688a2a247ab4f070fcf4a" - integrity sha512-e3sc1UzakF+bWquC8/dbPCgo7LgPEW1ekgwb4pmEcl8tOc/I7lML8r7HBql+b0VRk7tJWTZqtkeObwJAVR1pxg== - negotiator@0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" diff --git a/alchemist-webgui/build.gradle.kts b/alchemist-webgui/build.gradle.kts index 65972b7910..f4e40d5a30 100644 --- a/alchemist-webgui/build.gradle.kts +++ b/alchemist-webgui/build.gradle.kts @@ -91,6 +91,7 @@ kotlin { implementation(libs.kotlin.coroutines.core) implementation(libs.apollo.runtime) implementation(libs.kotlin.stdlib) + implementation(libs.korim) implementation(libs.redux.kotlin.threadsafe) } } diff --git a/alchemist-webgui/src/jsMain/kotlin/Client.kt b/alchemist-webgui/src/jsMain/kotlin/Client.kt index 9171353cf7..d34d18692f 100644 --- a/alchemist-webgui/src/jsMain/kotlin/Client.kt +++ b/alchemist-webgui/src/jsMain/kotlin/Client.kt @@ -11,15 +11,8 @@ import io.kvision.BootstrapCssModule import io.kvision.BootstrapIconsModule import io.kvision.BootstrapModule import io.kvision.BootstrapUploadModule -import io.kvision.ChartModule import io.kvision.CoreModule -import io.kvision.DatetimeModule import io.kvision.FontAwesomeModule -import io.kvision.ImaskModule -import io.kvision.MapsModule -import io.kvision.RichTextModule -import io.kvision.TabulatorCssBootstrapModule -import io.kvision.TabulatorModule import io.kvision.ToastifyModule import io.kvision.TomSelectModule import io.kvision.module @@ -36,18 +29,11 @@ fun main() { module.hot, BootstrapModule, BootstrapCssModule, - DatetimeModule, - RichTextModule, TomSelectModule, BootstrapUploadModule, - ImaskModule, ToastifyModule, FontAwesomeModule, BootstrapIconsModule, - ChartModule, - TabulatorModule, - TabulatorCssBootstrapModule, - MapsModule, CoreModule, ) } From 32cc4422c727ff7fa1e50129059ede008cbbf58d Mon Sep 17 00:00:00 2001 From: Tiziano Vuksan Date: Fri, 16 Feb 2024 11:20:34 +0100 Subject: [PATCH 12/22] chore(webgui): simplified click hanlder for play button --- alchemist-webgui/build.gradle.kts | 1 - .../kotlin/components/navbar/PlayButton.kt | 23 +++++-------------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/alchemist-webgui/build.gradle.kts b/alchemist-webgui/build.gradle.kts index f4e40d5a30..9d3ffb3d7d 100644 --- a/alchemist-webgui/build.gradle.kts +++ b/alchemist-webgui/build.gradle.kts @@ -101,7 +101,6 @@ kotlin { implementation(incarnation("sapere")) implementation(incarnation("protelis")) implementation(alchemist("cognitive-agents")) - implementation(alchemist("full")) implementation("io.ktor:ktor-server-html-builder:2.3.8") implementation(libs.bundles.ktor.server) } diff --git a/alchemist-webgui/src/jsMain/kotlin/components/navbar/PlayButton.kt b/alchemist-webgui/src/jsMain/kotlin/components/navbar/PlayButton.kt index 29a4782494..9d5ac8af20 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/navbar/PlayButton.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/navbar/PlayButton.kt @@ -63,23 +63,12 @@ class PlayButton(text: String) : Button(text) { onClick { CoroutineScope(Dispatchers.Default).launch { - when (simulationStatus) { - SimState.READY -> { - SimulationControlApi.playSimulation() - EnvironmentStore.callEnvironmentSubscription() - } - SimState.PAUSED -> { - SimulationControlApi.playSimulation() - EnvironmentStore.callEnvironmentSubscription() - } - SimState.RUNNING -> { - SimulationControlApi.pauseSimulation() - // EnvironmentStore.cancelSubscription() - } - else -> { - SimulationControlApi.terminateSimulation() - // EnvironmentStore.cancelSubscription() - } + if (simulationStatus == SimState.READY || simulationStatus == SimState.PAUSED) { + SimulationControlApi.playSimulation() + EnvironmentStore.callEnvironmentSubscription() + } else if (simulationStatus == SimState.RUNNING) { + SimulationControlApi.pauseSimulation() + // EnvironmentStore.cancelSubscription() } SimulationStatus.callGetStatus() } From cad396c90a12e0d1a2c94d33023bbc335d42034f Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Fri, 16 Feb 2024 16:59:40 +0100 Subject: [PATCH 13/22] Delete javadoc-io.json.orig --- javadoc-io.json.orig | 993 ------------------------------------------- 1 file changed, 993 deletions(-) delete mode 100644 javadoc-io.json.orig diff --git a/javadoc-io.json.orig b/javadoc-io.json.orig deleted file mode 100644 index f942b8d35a..0000000000 --- a/javadoc-io.json.orig +++ /dev/null @@ -1,993 +0,0 @@ -{ - "ch.qos.logback/logback-classic/1.2.11": { - "first": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.2.11", - "second": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.2.11/package-list" - }, - "ch.qos.logback/logback-classic/1.4.0": { - "first": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.0", - "second": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.0/element-list" - }, - "ch.qos.logback/logback-classic/1.4.1": { - "first": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.1", - "second": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.1/element-list" - }, - "ch.qos.logback/logback-classic/1.4.14": { - "first": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.14", - "second": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.14/element-list" - }, - "ch.qos.logback/logback-classic/1.4.2": { - "first": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.2", - "second": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.2/element-list" - }, - "ch.qos.logback/logback-classic/1.4.3": { - "first": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.3", - "second": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.3/element-list" - }, - "ch.qos.logback/logback-classic/1.4.4": { - "first": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.4", - "second": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.4/element-list" - }, - "ch.qos.logback/logback-classic/1.4.5": { - "first": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.5", - "second": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.5/element-list" - }, - "ch.qos.logback/logback-classic/1.4.6": { - "first": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.6", - "second": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.6/element-list" - }, - "ch.qos.logback/logback-classic/1.4.7": { - "first": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.7", - "second": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.7/element-list" - }, - "ch.qos.logback/logback-classic/1.4.8": { - "first": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.8", - "second": "https://javadoc.io/doc/ch.qos.logback/logback-classic/1.4.8/element-list" - }, -<<<<<<< HEAD - "com.apollographql.apollo3/apollo-runtime/4.0.0-beta.4": { - "first": "https://javadoc.io/doc/com.apollographql.apollo3/apollo-runtime/4.0.0-beta.4" - }, -======= ->>>>>>> depfixPR/master - "com.expediagroup/graphql-kotlin-hooks-provider/7.0.2": { - "first": "https://javadoc.io/doc/com.expediagroup/graphql-kotlin-hooks-provider/7.0.2" - }, - "com.expediagroup/graphql-kotlin-ktor-server/7.0.2": { - "first": "https://javadoc.io/doc/com.expediagroup/graphql-kotlin-ktor-server/7.0.2", - "second": "https://javadoc.io/doc/com.expediagroup/graphql-kotlin-ktor-server/7.0.2/package-list" - }, - "com.expediagroup/graphql-kotlin-server/7.0.2": { - "first": "https://javadoc.io/doc/com.expediagroup/graphql-kotlin-server/7.0.2", - "second": "https://javadoc.io/doc/com.expediagroup/graphql-kotlin-server/7.0.2/package-list" - }, - "com.github.ben-manes.caffeine/caffeine/3.0.5": { - "first": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.0.5", - "second": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.0.5/element-list" - }, - "com.github.ben-manes.caffeine/caffeine/3.0.6": { - "first": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.0.6", - "second": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.0.6/element-list" - }, - "com.github.ben-manes.caffeine/caffeine/3.1.0": { - "first": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.1.0", - "second": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.1.0/element-list" - }, - "com.github.ben-manes.caffeine/caffeine/3.1.1": { - "first": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.1.1", - "second": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.1.1/element-list" - }, - "com.github.ben-manes.caffeine/caffeine/3.1.2": { - "first": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.1.2", - "second": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.1.2/element-list" - }, - "com.github.ben-manes.caffeine/caffeine/3.1.4": { - "first": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.1.4", - "second": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.1.4/element-list" - }, - "com.github.ben-manes.caffeine/caffeine/3.1.5": { - "first": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.1.5", - "second": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.1.5/element-list" - }, - "com.github.ben-manes.caffeine/caffeine/3.1.6": { - "first": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.1.6", - "second": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.1.6/element-list" - }, - "com.github.ben-manes.caffeine/caffeine/3.1.7": { - "first": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.1.7", - "second": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.1.7/element-list" - }, - "com.github.ben-manes.caffeine/caffeine/3.1.8": { - "first": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.1.8", - "second": "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/3.1.8/element-list" - }, - "com.github.cb372/scalacache-core_2.13/0.28.0": { - "first": "https://javadoc.io/doc/com.github.cb372/scalacache-core_2.13/0.28.0" - }, - "com.github.cb372/scalacache-guava_2.13/0.28.0": { - "first": "https://javadoc.io/doc/com.github.cb372/scalacache-guava_2.13/0.28.0" - }, - "com.github.davidmoten/rtree/0.10": { - "first": "https://javadoc.io/doc/com.github.davidmoten/rtree/0.10", - "second": "https://javadoc.io/doc/com.github.davidmoten/rtree/0.10/package-list" - }, - "com.github.davidmoten/rtree/0.11": { - "first": "https://javadoc.io/doc/com.github.davidmoten/rtree/0.11", - "second": "https://javadoc.io/doc/com.github.davidmoten/rtree/0.11/package-list" - }, - "com.github.davidmoten/rtree/0.8.7": { - "first": "https://javadoc.io/doc/com.github.davidmoten/rtree/0.8.7", - "second": "https://javadoc.io/doc/com.github.davidmoten/rtree/0.8.7/package-list" - }, - "com.github.davidmoten/rtree/0.9": { - "first": "https://javadoc.io/doc/com.github.davidmoten/rtree/0.9", - "second": "https://javadoc.io/doc/com.github.davidmoten/rtree/0.9/element-list" - }, - "com.github.jiconfont/jiconfont-google_material_design_icons/2.2.0.2": { - "first": "https://javadoc.io/doc/com.github.jiconfont/jiconfont-google_material_design_icons/2.2.0.2", - "second": "https://javadoc.io/doc/com.github.jiconfont/jiconfont-google_material_design_icons/2.2.0.2/package-list" - }, - "com.github.jiconfont/jiconfont-javafx/1.0.0": { - "first": "https://javadoc.io/doc/com.github.jiconfont/jiconfont-javafx/1.0.0", - "second": "https://javadoc.io/doc/com.github.jiconfont/jiconfont-javafx/1.0.0/package-list" - }, - "com.google.code.gson/gson/2.10": { - "first": "https://javadoc.io/doc/com.google.code.gson/gson/2.10", - "second": "https://javadoc.io/doc/com.google.code.gson/gson/2.10/element-list" - }, - "com.google.code.gson/gson/2.10.1": { - "first": "https://javadoc.io/doc/com.google.code.gson/gson/2.10.1", - "second": "https://javadoc.io/doc/com.google.code.gson/gson/2.10.1/element-list" - }, - "com.google.code.gson/gson/2.8.9": { - "first": "https://javadoc.io/doc/com.google.code.gson/gson/2.8.9", - "second": "https://javadoc.io/doc/com.google.code.gson/gson/2.8.9/element-list" - }, - "com.google.code.gson/gson/2.9.0": { - "first": "https://javadoc.io/doc/com.google.code.gson/gson/2.9.0", - "second": "https://javadoc.io/doc/com.google.code.gson/gson/2.9.0/element-list" - }, - "com.google.code.gson/gson/2.9.1": { - "first": "https://javadoc.io/doc/com.google.code.gson/gson/2.9.1", - "second": "https://javadoc.io/doc/com.google.code.gson/gson/2.9.1/element-list" - }, - "com.google.guava/guava/31.0.1-jre": { - "first": "https://javadoc.io/doc/com.google.guava/guava/31.0.1-jre", - "second": "https://javadoc.io/doc/com.google.guava/guava/31.0.1-jre/element-list" - }, - "com.google.guava/guava/31.1-jre": { - "first": "https://javadoc.io/doc/com.google.guava/guava/31.1-jre", - "second": "https://javadoc.io/doc/com.google.guava/guava/31.1-jre/element-list" - }, - "com.google.guava/guava/32.1.1-jre": { - "first": "https://javadoc.io/doc/com.google.guava/guava/32.1.1-jre", - "second": "https://javadoc.io/doc/com.google.guava/guava/32.1.1-jre/element-list" - }, - "com.google.guava/guava/32.1.3-jre": { - "first": "https://javadoc.io/doc/com.google.guava/guava/32.1.3-jre", - "second": "https://javadoc.io/doc/com.google.guava/guava/32.1.3-jre/element-list" - }, - "com.google.guava/guava/33.0.0-jre": { - "first": "https://javadoc.io/doc/com.google.guava/guava/33.0.0-jre", - "second": "https://javadoc.io/doc/com.google.guava/guava/33.0.0-jre/element-list" - }, - "com.graphhopper/graphhopper-core/4.0": { - "first": "https://javadoc.io/doc/com.graphhopper/graphhopper-core/4.0", - "second": "https://javadoc.io/doc/com.graphhopper/graphhopper-core/4.0/package-list" - }, - "com.graphhopper/graphhopper-core/5.0": { - "first": "https://javadoc.io/doc/com.graphhopper/graphhopper-core/5.0", - "second": "https://javadoc.io/doc/com.graphhopper/graphhopper-core/5.0/package-list" - }, - "com.graphhopper/graphhopper-core/5.3": { - "first": "https://javadoc.io/doc/com.graphhopper/graphhopper-core/5.3", - "second": "https://javadoc.io/doc/com.graphhopper/graphhopper-core/5.3/package-list" - }, - "com.graphhopper/graphhopper-core/7.0": { - "first": "https://javadoc.io/doc/com.graphhopper/graphhopper-core/7.0", - "second": "https://javadoc.io/doc/com.graphhopper/graphhopper-core/7.0/package-list" - }, - "com.graphhopper/graphhopper-core/7.0-pre1": { - "first": "https://javadoc.io/doc/com.graphhopper/graphhopper-core/7.0-pre1", - "second": "https://javadoc.io/doc/com.graphhopper/graphhopper-core/7.0-pre1/package-list" - }, - "com.graphhopper/graphhopper-core/7.0-testgithub3": { - "first": "https://javadoc.io/doc/com.graphhopper/graphhopper-core/7.0-testgithub3", - "second": "https://javadoc.io/doc/com.graphhopper/graphhopper-core/7.0-testgithub3/package-list" - }, - "com.graphhopper/graphhopper-core/7.0-testgithub6": { - "first": "https://javadoc.io/doc/com.graphhopper/graphhopper-core/7.0-testgithub6", - "second": "https://javadoc.io/doc/com.graphhopper/graphhopper-core/7.0-testgithub6/package-list" - }, - "com.graphhopper/graphhopper-core/9.0-pre1": { - "first": "https://javadoc.io/doc/com.graphhopper/graphhopper-core/9.0-pre1", - "second": "https://javadoc.io/doc/com.graphhopper/graphhopper-core/9.0-pre1/element-list" - }, - "com.javadocmd/simplelatlng/1.3.1": { - "first": "https://javadoc.io/doc/com.javadocmd/simplelatlng/1.3.1", - "second": "https://javadoc.io/doc/com.javadocmd/simplelatlng/1.3.1/package-list" - }, - "com.javadocmd/simplelatlng/1.4.0": { - "first": "https://javadoc.io/doc/com.javadocmd/simplelatlng/1.4.0" - }, - "com.jfoenix/jfoenix/9.0.10": { - "first": "https://javadoc.io/doc/com.jfoenix/jfoenix/9.0.10", - "second": "https://javadoc.io/doc/com.jfoenix/jfoenix/9.0.10/package-list" - }, - "com.miglayout/miglayout-swing/11.0": { - "first": "https://javadoc.io/doc/com.miglayout/miglayout-swing/11.0", - "second": "https://javadoc.io/doc/com.miglayout/miglayout-swing/11.0/element-list" - }, - "com.miglayout/miglayout-swing/11.1": { - "first": "https://javadoc.io/doc/com.miglayout/miglayout-swing/11.1" - }, - "com.miglayout/miglayout-swing/11.2": { - "first": "https://javadoc.io/doc/com.miglayout/miglayout-swing/11.2", - "second": "https://javadoc.io/doc/com.miglayout/miglayout-swing/11.2/element-list" - }, - "com.miglayout/miglayout-swing/11.3": { - "first": "https://javadoc.io/doc/com.miglayout/miglayout-swing/11.3", - "second": "https://javadoc.io/doc/com.miglayout/miglayout-swing/11.3/element-list" - }, - "com.uchuhimo/konf/1.1.2": { - "first": "https://javadoc.io/doc/com.uchuhimo/konf/1.1.2" - }, - "commons-cli/commons-cli/1.5.0": { - "first": "https://javadoc.io/doc/commons-cli/commons-cli/1.5.0", - "second": "https://javadoc.io/doc/commons-cli/commons-cli/1.5.0/package-list" - }, - "commons-cli/commons-cli/1.6.0": { - "first": "https://javadoc.io/doc/commons-cli/commons-cli/1.6.0", - "second": "https://javadoc.io/doc/commons-cli/commons-cli/1.6.0/element-list" - }, - "commons-codec/commons-codec/1.15": { - "first": "https://javadoc.io/doc/commons-codec/commons-codec/1.15", - "second": "https://javadoc.io/doc/commons-codec/commons-codec/1.15/package-list" - }, - "commons-codec/commons-codec/1.16.0": { - "first": "https://javadoc.io/doc/commons-codec/commons-codec/1.16.0", - "second": "https://javadoc.io/doc/commons-codec/commons-codec/1.16.0/package-list" - }, - "commons-io/commons-io/2.11.0": { - "first": "https://javadoc.io/doc/commons-io/commons-io/2.11.0", - "second": "https://javadoc.io/doc/commons-io/commons-io/2.11.0/package-list" - }, - "commons-io/commons-io/2.13.0": { - "first": "https://javadoc.io/doc/commons-io/commons-io/2.13.0", - "second": "https://javadoc.io/doc/commons-io/commons-io/2.13.0/package-list" - }, - "commons-io/commons-io/2.15.0": { - "first": "https://javadoc.io/doc/commons-io/commons-io/2.15.0", - "second": "https://javadoc.io/doc/commons-io/commons-io/2.15.0/element-list" - }, - "commons-io/commons-io/2.15.1": { - "first": "https://javadoc.io/doc/commons-io/commons-io/2.15.1", - "second": "https://javadoc.io/doc/commons-io/commons-io/2.15.1/element-list" - }, - "de.codecentric.centerdevice/javafxsvg/1.3.0": { - "first": "https://javadoc.io/doc/de.codecentric.centerdevice/javafxsvg/1.3.0", - "second": "https://javadoc.io/doc/de.codecentric.centerdevice/javafxsvg/1.3.0/package-list" - }, - "guru.nidi.com.kitfox/svgSalamander/1.1.3": { - "first": "https://javadoc.io/doc/guru.nidi.com.kitfox/svgSalamander/1.1.3" - }, - "io.arrow-kt/arrow-core/1.0.1": { - "first": "https://javadoc.io/doc/io.arrow-kt/arrow-core/1.0.1" - }, - "io.arrow-kt/arrow-core/1.1.2": { - "first": "https://javadoc.io/doc/io.arrow-kt/arrow-core/1.1.2" - }, - "io.arrow-kt/arrow-core/1.1.3": { - "first": "https://javadoc.io/doc/io.arrow-kt/arrow-core/1.1.3" - }, - "io.arrow-kt/arrow-core/1.1.5": { - "first": "https://javadoc.io/doc/io.arrow-kt/arrow-core/1.1.5" - }, - "io.arrow-kt/arrow-core/1.2.0": { - "first": "https://javadoc.io/doc/io.arrow-kt/arrow-core/1.2.0" - }, - "io.arrow-kt/arrow-core/1.2.1": { - "first": "https://javadoc.io/doc/io.arrow-kt/arrow-core/1.2.1" - }, - "io.github.classgraph/classgraph/4.8.138": { - "first": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.138", - "second": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.138/package-list" - }, - "io.github.classgraph/classgraph/4.8.143": { - "first": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.143", - "second": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.143/package-list" - }, - "io.github.classgraph/classgraph/4.8.146": { - "first": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.146", - "second": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.146/package-list" - }, - "io.github.classgraph/classgraph/4.8.147": { - "first": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.147", - "second": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.147/package-list" - }, - "io.github.classgraph/classgraph/4.8.148": { - "first": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.148" - }, - "io.github.classgraph/classgraph/4.8.149": { - "first": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.149" - }, - "io.github.classgraph/classgraph/4.8.150": { - "first": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.150", - "second": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.150/package-list" - }, - "io.github.classgraph/classgraph/4.8.151": { - "first": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.151", - "second": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.151/package-list" - }, - "io.github.classgraph/classgraph/4.8.152": { - "first": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.152", - "second": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.152/package-list" - }, - "io.github.classgraph/classgraph/4.8.153": { - "first": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.153", - "second": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.153/package-list" - }, - "io.github.classgraph/classgraph/4.8.154": { - "first": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.154", - "second": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.154/package-list" - }, - "io.github.classgraph/classgraph/4.8.155": { - "first": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.155", - "second": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.155/package-list" - }, - "io.github.classgraph/classgraph/4.8.156": { - "first": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.156" - }, - "io.github.classgraph/classgraph/4.8.157": { - "first": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.157", - "second": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.157/package-list" - }, - "io.github.classgraph/classgraph/4.8.158": { - "first": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.158", - "second": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.158/package-list" - }, - "io.github.classgraph/classgraph/4.8.161": { - "first": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.161", - "second": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.161/package-list" - }, - "io.github.classgraph/classgraph/4.8.165": { - "first": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.165", - "second": "https://javadoc.io/doc/io.github.classgraph/classgraph/4.8.165/element-list" - }, - "io.jenetics/jpx/2.3.0": { - "first": "https://javadoc.io/doc/io.jenetics/jpx/2.3.0", - "second": "https://javadoc.io/doc/io.jenetics/jpx/2.3.0/element-list" - }, - "io.jenetics/jpx/3.1.0": { - "first": "https://javadoc.io/doc/io.jenetics/jpx/3.1.0", - "second": "https://javadoc.io/doc/io.jenetics/jpx/3.1.0/element-list" - }, - "io.ktor/ktor-bom/2.3.7": { - "first": "https://javadoc.io/doc/io.ktor/ktor-bom/2.3.7" - }, - "it.unibo.scafi/scafi-core_2.13/1.1.7": { - "first": "https://javadoc.io/doc/it.unibo.scafi/scafi-core_2.13/1.1.7" - }, - "it.unibo.scafi/scafi-core_2.13/1.2.0": { - "first": "https://javadoc.io/doc/it.unibo.scafi/scafi-core_2.13/1.2.0" - }, - "it.unimi.dsi/dsiutils/2.6.17": { - "first": "https://javadoc.io/doc/it.unimi.dsi/dsiutils/2.6.17", - "second": "https://javadoc.io/doc/it.unimi.dsi/dsiutils/2.6.17/element-list" - }, - "it.unimi.dsi/dsiutils/2.7.0": { - "first": "https://javadoc.io/doc/it.unimi.dsi/dsiutils/2.7.0", - "second": "https://javadoc.io/doc/it.unimi.dsi/dsiutils/2.7.0/element-list" - }, - "it.unimi.dsi/dsiutils/2.7.1": { - "first": "https://javadoc.io/doc/it.unimi.dsi/dsiutils/2.7.1", - "second": "https://javadoc.io/doc/it.unimi.dsi/dsiutils/2.7.1/element-list" - }, - "it.unimi.dsi/dsiutils/2.7.2": { - "first": "https://javadoc.io/doc/it.unimi.dsi/dsiutils/2.7.2" - }, - "it.unimi.dsi/dsiutils/2.7.3": { - "first": "https://javadoc.io/doc/it.unimi.dsi/dsiutils/2.7.3" - }, - "net.anwiba.commons.swing.icons/org.oxygen.oxygen-icons/4.13.0-1.1.158": { - "first": "https://javadoc.io/doc/net.anwiba.commons.swing.icons/org.oxygen.oxygen-icons/4.13.0-1.1.158" - }, - "net.anwiba.commons.swing.icons/org.oxygen.oxygen-icons/4.13.0-1.2.50": { - "first": "https://javadoc.io/doc/net.anwiba.commons.swing.icons/org.oxygen.oxygen-icons/4.13.0-1.2.50" - }, - "net.harawata/appdirs/1.2.1": { - "first": "https://javadoc.io/doc/net.harawata/appdirs/1.2.1", - "second": "https://javadoc.io/doc/net.harawata/appdirs/1.2.1/package-list" - }, - "net.harawata/appdirs/1.2.2": { - "first": "https://javadoc.io/doc/net.harawata/appdirs/1.2.2", - "second": "https://javadoc.io/doc/net.harawata/appdirs/1.2.2/element-list" - }, - "net.pearx.kasechange/kasechange/1.4.1": { - "first": "https://javadoc.io/doc/net.pearx.kasechange/kasechange/1.4.1" - }, - "net.sf.trove4j/trove4j/3.0.3": { - "first": "https://javadoc.io/doc/net.sf.trove4j/trove4j/3.0.3", - "second": "https://javadoc.io/doc/net.sf.trove4j/trove4j/3.0.3/package-list" - }, - "no.tornado/tornadofx/2.0.0-SNAPSHOT": { - "first": "https://javadoc.io/doc/no.tornado/tornadofx/2.0.0-SNAPSHOT" - }, - "org.apache.commons/commons-collections4/4.4": { - "first": "https://javadoc.io/doc/org.apache.commons/commons-collections4/4.4", - "second": "https://javadoc.io/doc/org.apache.commons/commons-collections4/4.4/package-list" - }, - "org.apache.commons/commons-lang3/3.12.0": { - "first": "https://javadoc.io/doc/org.apache.commons/commons-lang3/3.12.0", - "second": "https://javadoc.io/doc/org.apache.commons/commons-lang3/3.12.0/package-list" - }, - "org.apache.commons/commons-lang3/3.13.0": { - "first": "https://javadoc.io/doc/org.apache.commons/commons-lang3/3.13.0", - "second": "https://javadoc.io/doc/org.apache.commons/commons-lang3/3.13.0/package-list" - }, - "org.apache.commons/commons-lang3/3.14.0": { - "first": "https://javadoc.io/doc/org.apache.commons/commons-lang3/3.14.0", - "second": "https://javadoc.io/doc/org.apache.commons/commons-lang3/3.14.0/element-list" - }, - "org.apache.commons/commons-math3/3.6.1": { - "first": "https://javadoc.io/doc/org.apache.commons/commons-math3/3.6.1", - "second": "https://javadoc.io/doc/org.apache.commons/commons-math3/3.6.1/package-list" - }, - "org.apache.ignite/ignite-core/2.14.0": { - "first": "https://javadoc.io/doc/org.apache.ignite/ignite-core/2.14.0", - "second": "https://javadoc.io/doc/org.apache.ignite/ignite-core/2.14.0/package-list" - }, - "org.apache.ignite/ignite-core/2.15.0": { - "first": "https://javadoc.io/doc/org.apache.ignite/ignite-core/2.15.0", - "second": "https://javadoc.io/doc/org.apache.ignite/ignite-core/2.15.0/package-list" - }, - "org.apache.ignite/ignite-core/2.16.0": { - "first": "https://javadoc.io/doc/org.apache.ignite/ignite-core/2.16.0", - "second": "https://javadoc.io/doc/org.apache.ignite/ignite-core/2.16.0/package-list" - }, - "org.apache.ignite/ignite-indexing/2.12.0": { - "first": "https://javadoc.io/doc/org.apache.ignite/ignite-indexing/2.12.0", - "second": "https://javadoc.io/doc/org.apache.ignite/ignite-indexing/2.12.0/package-list" - }, - "org.apache.ignite/ignite-indexing/2.13.0": { - "first": "https://javadoc.io/doc/org.apache.ignite/ignite-indexing/2.13.0", - "second": "https://javadoc.io/doc/org.apache.ignite/ignite-indexing/2.13.0/package-list" - }, - "org.apache.ignite/ignite-indexing/2.14.0": { - "first": "https://javadoc.io/doc/org.apache.ignite/ignite-indexing/2.14.0" - }, - "org.apache.ignite/ignite-indexing/2.15.0": { - "first": "https://javadoc.io/doc/org.apache.ignite/ignite-indexing/2.15.0", - "second": "https://javadoc.io/doc/org.apache.ignite/ignite-indexing/2.15.0/package-list" - }, - "org.apache.ignite/ignite-indexing/2.16.0": { - "first": "https://javadoc.io/doc/org.apache.ignite/ignite-indexing/2.16.0", - "second": "https://javadoc.io/doc/org.apache.ignite/ignite-indexing/2.16.0/package-list" - }, - "org.apache.ignite/ignite-spring/2.12.0": { - "first": "https://javadoc.io/doc/org.apache.ignite/ignite-spring/2.12.0", - "second": "https://javadoc.io/doc/org.apache.ignite/ignite-spring/2.12.0/package-list" - }, - "org.apache.ignite/ignite-spring/2.13.0": { - "first": "https://javadoc.io/doc/org.apache.ignite/ignite-spring/2.13.0", - "second": "https://javadoc.io/doc/org.apache.ignite/ignite-spring/2.13.0/package-list" - }, - "org.apache.ignite/ignite-spring/2.14.0": { - "first": "https://javadoc.io/doc/org.apache.ignite/ignite-spring/2.14.0" - }, - "org.apache.ignite/ignite-spring/2.15.0": { - "first": "https://javadoc.io/doc/org.apache.ignite/ignite-spring/2.15.0", - "second": "https://javadoc.io/doc/org.apache.ignite/ignite-spring/2.15.0/package-list" - }, - "org.apache.ignite/ignite-spring/2.16.0": { - "first": "https://javadoc.io/doc/org.apache.ignite/ignite-spring/2.16.0", - "second": "https://javadoc.io/doc/org.apache.ignite/ignite-spring/2.16.0/package-list" - }, - "org.controlsfx/controlsfx/11.1.1": { - "first": "https://javadoc.io/doc/org.controlsfx/controlsfx/11.1.1", - "second": "https://javadoc.io/doc/org.controlsfx/controlsfx/11.1.1/element-list" - }, - "org.controlsfx/controlsfx/11.1.2": { - "first": "https://javadoc.io/doc/org.controlsfx/controlsfx/11.1.2", - "second": "https://javadoc.io/doc/org.controlsfx/controlsfx/11.1.2/element-list" - }, - "org.controlsfx/controlsfx/11.2.0": { - "first": "https://javadoc.io/doc/org.controlsfx/controlsfx/11.2.0", - "second": "https://javadoc.io/doc/org.controlsfx/controlsfx/11.2.0/element-list" - }, - "org.danilopianini/boilerplate/0.2.2": { - "first": "https://javadoc.io/doc/org.danilopianini/boilerplate/0.2.2" - }, - "org.danilopianini/conrec/0.1.0": { - "first": "https://javadoc.io/doc/org.danilopianini/conrec/0.1.0" - }, - "org.danilopianini/de.saring.leafletmap/1.0.5": { - "first": "https://javadoc.io/doc/org.danilopianini/de.saring.leafletmap/1.0.5" - }, - "org.danilopianini/gson-extras/0.4.0": { - "first": "https://javadoc.io/doc/org.danilopianini/gson-extras/0.4.0", - "second": "https://javadoc.io/doc/org.danilopianini/gson-extras/0.4.0/package-list" - }, - "org.danilopianini/gson-extras/0.6.0": { - "first": "https://javadoc.io/doc/org.danilopianini/gson-extras/0.6.0", - "second": "https://javadoc.io/doc/org.danilopianini/gson-extras/0.6.0/package-list" - }, - "org.danilopianini/gson-extras/1.0.0": { - "first": "https://javadoc.io/doc/org.danilopianini/gson-extras/1.0.0", - "second": "https://javadoc.io/doc/org.danilopianini/gson-extras/1.0.0/package-list" - }, - "org.danilopianini/gson-extras/1.1.0": { - "first": "https://javadoc.io/doc/org.danilopianini/gson-extras/1.1.0", - "second": "https://javadoc.io/doc/org.danilopianini/gson-extras/1.1.0/package-list" - }, - "org.danilopianini/gson-extras/1.2.0": { - "first": "https://javadoc.io/doc/org.danilopianini/gson-extras/1.2.0", - "second": "https://javadoc.io/doc/org.danilopianini/gson-extras/1.2.0/package-list" - }, - "org.danilopianini/java-quadtree/0.2.0": { - "first": "https://javadoc.io/doc/org.danilopianini/java-quadtree/0.2.0", - "second": "https://javadoc.io/doc/org.danilopianini/java-quadtree/0.2.0/package-list" - }, - "org.danilopianini/javalib-java7/0.6.1": { - "first": "https://javadoc.io/doc/org.danilopianini/javalib-java7/0.6.1", - "second": "https://javadoc.io/doc/org.danilopianini/javalib-java7/0.6.1/package-list" - }, - "org.danilopianini/jirf/0.4.12": { - "first": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.12", - "second": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.12/element-list" - }, - "org.danilopianini/jirf/0.4.13": { - "first": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.13", - "second": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.13/element-list" - }, - "org.danilopianini/jirf/0.4.16": { - "first": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.16", - "second": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.16/element-list" - }, -<<<<<<< HEAD - "org.danilopianini/jirf/0.4.17": { - "first": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.17", - "second": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.17/element-list" -======= - "org.danilopianini/jirf/0.4.18": { - "first": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.18", - "second": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.18/element-list" ->>>>>>> depfixPR/master - }, - "org.danilopianini/jirf/0.4.5": { - "first": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.5", - "second": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.5/element-list" - }, - "org.danilopianini/jirf/0.4.6": { - "first": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.6", - "second": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.6/element-list" - }, - "org.danilopianini/jirf/0.4.7": { - "first": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.7", - "second": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.7/element-list" - }, - "org.danilopianini/jirf/0.4.8": { - "first": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.8", - "second": "https://javadoc.io/doc/org.danilopianini/jirf/0.4.8/element-list" - }, - "org.danilopianini/listset/0.3.9": { - "first": "https://javadoc.io/doc/org.danilopianini/listset/0.3.9", - "second": "https://javadoc.io/doc/org.danilopianini/listset/0.3.9/package-list" - }, - "org.danilopianini/thread-inheritable-resource-loader/0.3.5": { - "first": "https://javadoc.io/doc/org.danilopianini/thread-inheritable-resource-loader/0.3.5", - "second": "https://javadoc.io/doc/org.danilopianini/thread-inheritable-resource-loader/0.3.5/package-list" - }, - "org.danilopianini/thread-inheritable-resource-loader/0.3.6": { - "first": "https://javadoc.io/doc/org.danilopianini/thread-inheritable-resource-loader/0.3.6", - "second": "https://javadoc.io/doc/org.danilopianini/thread-inheritable-resource-loader/0.3.6/package-list" - }, - "org.dyn4j/dyn4j/5.0.1": { - "first": "https://javadoc.io/doc/org.dyn4j/dyn4j/5.0.1", - "second": "https://javadoc.io/doc/org.dyn4j/dyn4j/5.0.1/element-list" - }, - "org.graphstream/gs-algo/2.0": { - "first": "https://javadoc.io/doc/org.graphstream/gs-algo/2.0", - "second": "https://javadoc.io/doc/org.graphstream/gs-algo/2.0/element-list" - }, - "org.graphstream/gs-core/2.0": { - "first": "https://javadoc.io/doc/org.graphstream/gs-core/2.0", - "second": "https://javadoc.io/doc/org.graphstream/gs-core/2.0/element-list" - }, - "org.jetbrains.kotlin/kotlin-reflect/1.6.10": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-reflect/1.6.10" - }, - "org.jetbrains.kotlin/kotlin-reflect/1.6.20": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-reflect/1.6.20" - }, - "org.jetbrains.kotlin/kotlin-reflect/1.6.21": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-reflect/1.6.21" - }, - "org.jetbrains.kotlin/kotlin-reflect/1.7.0": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-reflect/1.7.0" - }, - "org.jetbrains.kotlin/kotlin-reflect/1.7.10": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-reflect/1.7.10" - }, - "org.jetbrains.kotlin/kotlin-reflect/1.7.20": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-reflect/1.7.20" - }, - "org.jetbrains.kotlin/kotlin-reflect/1.7.21": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-reflect/1.7.21" - }, - "org.jetbrains.kotlin/kotlin-reflect/1.7.22": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-reflect/1.7.22" - }, - "org.jetbrains.kotlin/kotlin-reflect/1.8.0": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-reflect/1.8.0" - }, - "org.jetbrains.kotlin/kotlin-reflect/1.8.10": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-reflect/1.8.10" - }, - "org.jetbrains.kotlin/kotlin-reflect/1.8.21": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-reflect/1.8.21" - }, - "org.jetbrains.kotlin/kotlin-reflect/1.8.22": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-reflect/1.8.22" - }, - "org.jetbrains.kotlin/kotlin-reflect/1.9.21": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-reflect/1.9.21" - }, - "org.jetbrains.kotlin/kotlin-reflect/1.9.22": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-reflect/1.9.22" - }, - "org.jetbrains.kotlin/kotlin-reflect/null": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-reflect/null" - }, - "org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.6.10": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.6.10" - }, - "org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.6.20": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.6.20" - }, - "org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.6.21": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.6.21" - }, - "org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.7.0": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.7.0" - }, - "org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.7.10": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.7.10" - }, - "org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.7.20": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.7.20" - }, - "org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.7.21": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.7.21" - }, - "org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.7.22": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.7.22" - }, - "org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.8.0": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.8.0" - }, - "org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.8.10": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.8.10" - }, - "org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.8.21": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.8.21" - }, - "org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.8.22": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.8.22" - }, - "org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.9.21": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.9.21" - }, - "org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.9.22": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.9.22" - }, - "org.jetbrains.kotlin/kotlin-stdlib-jdk8/null": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib-jdk8/null" - }, - "org.jetbrains.kotlin/kotlin-stdlib/1.9.22": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlin/kotlin-stdlib/1.9.22" - }, - "org.jetbrains.kotlinx/kotlinx-cli/0.3.5": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlinx/kotlinx-cli/0.3.5" - }, - "org.jetbrains.kotlinx/kotlinx-cli/0.3.6": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlinx/kotlinx-cli/0.3.6" - }, - "org.jetbrains.kotlinx/kotlinx-coroutines-core/1.6.0": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.6.0" - }, - "org.jetbrains.kotlinx/kotlinx-coroutines-core/1.6.1": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.6.1" - }, - "org.jetbrains.kotlinx/kotlinx-coroutines-core/1.6.2": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.6.2" - }, - "org.jetbrains.kotlinx/kotlinx-coroutines-core/1.6.3": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.6.3" - }, - "org.jetbrains.kotlinx/kotlinx-coroutines-core/1.6.4": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.6.4" - }, - "org.jetbrains.kotlinx/kotlinx-coroutines-core/1.7.0": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.7.0" - }, - "org.jetbrains.kotlinx/kotlinx-coroutines-core/1.7.1": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.7.1" - }, - "org.jetbrains.kotlinx/kotlinx-coroutines-core/1.7.3": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlinx/kotlinx-coroutines-core/1.7.3" - }, - "org.jetbrains.kotlinx/kotlinx-serialization-json/1.6.2": { - "first": "https://javadoc.io/doc/org.jetbrains.kotlinx/kotlinx-serialization-json/1.6.2" - }, - "org.jgrapht/jgrapht-core/1.5.1": { - "first": "https://javadoc.io/doc/org.jgrapht/jgrapht-core/1.5.1", - "second": "https://javadoc.io/doc/org.jgrapht/jgrapht-core/1.5.1/element-list" - }, - "org.jgrapht/jgrapht-core/1.5.2": { - "first": "https://javadoc.io/doc/org.jgrapht/jgrapht-core/1.5.2", - "second": "https://javadoc.io/doc/org.jgrapht/jgrapht-core/1.5.2/element-list" - }, - "org.jooq/jool/0.9.15": { - "first": "https://javadoc.io/doc/org.jooq/jool/0.9.15", - "second": "https://javadoc.io/doc/org.jooq/jool/0.9.15/element-list" - }, - "org.mapsforge/mapsforge-map-awt/0.17.0": { - "first": "https://javadoc.io/doc/org.mapsforge/mapsforge-map-awt/0.17.0", - "second": "https://javadoc.io/doc/org.mapsforge/mapsforge-map-awt/0.17.0/element-list" - }, - "org.mapsforge/mapsforge-map-awt/0.18.0": { - "first": "https://javadoc.io/doc/org.mapsforge/mapsforge-map-awt/0.18.0", - "second": "https://javadoc.io/doc/org.mapsforge/mapsforge-map-awt/0.18.0/element-list" - }, - "org.mapsforge/mapsforge-map-awt/0.19.0": { - "first": "https://javadoc.io/doc/org.mapsforge/mapsforge-map-awt/0.19.0", - "second": "https://javadoc.io/doc/org.mapsforge/mapsforge-map-awt/0.19.0/element-list" - }, - "org.mapsforge/mapsforge-map-awt/0.20.0": { - "first": "https://javadoc.io/doc/org.mapsforge/mapsforge-map-awt/0.20.0", - "second": "https://javadoc.io/doc/org.mapsforge/mapsforge-map-awt/0.20.0/element-list" - }, - "org.mongodb/mongodb-driver-sync/4.10.2": { - "first": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.10.2", - "second": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.10.2/element-list" - }, - "org.mongodb/mongodb-driver-sync/4.11.1": { - "first": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.11.1", - "second": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.11.1/element-list" - }, - "org.mongodb/mongodb-driver-sync/4.4.1": { - "first": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.4.1", - "second": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.4.1/element-list" - }, - "org.mongodb/mongodb-driver-sync/4.5.1": { - "first": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.5.1", - "second": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.5.1/element-list" - }, - "org.mongodb/mongodb-driver-sync/4.6.0": { - "first": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.6.0", - "second": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.6.0/element-list" - }, - "org.mongodb/mongodb-driver-sync/4.6.1": { - "first": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.6.1", - "second": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.6.1/element-list" - }, - "org.mongodb/mongodb-driver-sync/4.7.0": { - "first": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.7.0" - }, - "org.mongodb/mongodb-driver-sync/4.7.1": { - "first": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.7.1" - }, - "org.mongodb/mongodb-driver-sync/4.7.2": { - "first": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.7.2", - "second": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.7.2/element-list" - }, - "org.mongodb/mongodb-driver-sync/4.8.0": { - "first": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.8.0", - "second": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.8.0/element-list" - }, - "org.mongodb/mongodb-driver-sync/4.8.1": { - "first": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.8.1", - "second": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.8.1/element-list" - }, - "org.mongodb/mongodb-driver-sync/4.8.2": { - "first": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.8.2", - "second": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.8.2/element-list" - }, - "org.mongodb/mongodb-driver-sync/4.9.0": { - "first": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.9.0", - "second": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.9.0/element-list" - }, - "org.mongodb/mongodb-driver-sync/4.9.1": { - "first": "https://javadoc.io/doc/org.mongodb/mongodb-driver-sync/4.9.1" - }, - "org.openjfx/javafx-base/11": { - "first": "https://javadoc.io/doc/org.openjfx/javafx-base/11", - "second": "https://javadoc.io/doc/org.openjfx/javafx-base/11/element-list" - }, - "org.openjfx/javafx-controls/11": { - "first": "https://javadoc.io/doc/org.openjfx/javafx-controls/11", - "second": "https://javadoc.io/doc/org.openjfx/javafx-controls/11/element-list" - }, - "org.openjfx/javafx-fxml/11": { - "first": "https://javadoc.io/doc/org.openjfx/javafx-fxml/11", - "second": "https://javadoc.io/doc/org.openjfx/javafx-fxml/11/element-list" - }, - "org.openjfx/javafx-graphics/11": { - "first": "https://javadoc.io/doc/org.openjfx/javafx-graphics/11", - "second": "https://javadoc.io/doc/org.openjfx/javafx-graphics/11/element-list" - }, - "org.openjfx/javafx-media/11": { - "first": "https://javadoc.io/doc/org.openjfx/javafx-media/11", - "second": "https://javadoc.io/doc/org.openjfx/javafx-media/11/element-list" - }, - "org.openjfx/javafx-swing/11": { - "first": "https://javadoc.io/doc/org.openjfx/javafx-swing/11", - "second": "https://javadoc.io/doc/org.openjfx/javafx-swing/11/element-list" - }, - "org.openjfx/javafx-web/11": { - "first": "https://javadoc.io/doc/org.openjfx/javafx-web/11", - "second": "https://javadoc.io/doc/org.openjfx/javafx-web/11/element-list" - }, - "org.protelis/protelis-interpreter/17.0.0": { - "first": "https://javadoc.io/doc/org.protelis/protelis-interpreter/17.0.0", - "second": "https://javadoc.io/doc/org.protelis/protelis-interpreter/17.0.0/element-list" - }, - "org.protelis/protelis-interpreter/17.0.4": { - "first": "https://javadoc.io/doc/org.protelis/protelis-interpreter/17.0.4", - "second": "https://javadoc.io/doc/org.protelis/protelis-interpreter/17.0.4/element-list" - }, - "org.protelis/protelis-interpreter/17.3.11": { - "first": "https://javadoc.io/doc/org.protelis/protelis-interpreter/17.3.11", - "second": "https://javadoc.io/doc/org.protelis/protelis-interpreter/17.3.11/element-list" - }, - "org.protelis/protelis-interpreter/17.3.12": { - "first": "https://javadoc.io/doc/org.protelis/protelis-interpreter/17.3.12", - "second": "https://javadoc.io/doc/org.protelis/protelis-interpreter/17.3.12/element-list" - }, - "org.protelis/protelis-interpreter/17.3.5": { - "first": "https://javadoc.io/doc/org.protelis/protelis-interpreter/17.3.5", - "second": "https://javadoc.io/doc/org.protelis/protelis-interpreter/17.3.5/element-list" - }, - "org.protelis/protelis-interpreter/17.3.9": { - "first": "https://javadoc.io/doc/org.protelis/protelis-interpreter/17.3.9", - "second": "https://javadoc.io/doc/org.protelis/protelis-interpreter/17.3.9/element-list" - }, - "org.protelis/protelis-lang/17.0.0": { - "first": "https://javadoc.io/doc/org.protelis/protelis-lang/17.0.0", - "second": "https://javadoc.io/doc/org.protelis/protelis-lang/17.0.0/package-list" - }, - "org.protelis/protelis-lang/17.0.4": { - "first": "https://javadoc.io/doc/org.protelis/protelis-lang/17.0.4", - "second": "https://javadoc.io/doc/org.protelis/protelis-lang/17.0.4/package-list" - }, - "org.protelis/protelis-lang/17.3.11": { - "first": "https://javadoc.io/doc/org.protelis/protelis-lang/17.3.11", - "second": "https://javadoc.io/doc/org.protelis/protelis-lang/17.3.11/package-list" - }, - "org.protelis/protelis-lang/17.3.12": { - "first": "https://javadoc.io/doc/org.protelis/protelis-lang/17.3.12", - "second": "https://javadoc.io/doc/org.protelis/protelis-lang/17.3.12/package-list" - }, - "org.protelis/protelis-lang/17.3.5": { - "first": "https://javadoc.io/doc/org.protelis/protelis-lang/17.3.5", - "second": "https://javadoc.io/doc/org.protelis/protelis-lang/17.3.5/package-list" - }, - "org.protelis/protelis-lang/17.3.9": { - "first": "https://javadoc.io/doc/org.protelis/protelis-lang/17.3.9", - "second": "https://javadoc.io/doc/org.protelis/protelis-lang/17.3.9/package-list" - }, - "org.scala-lang/scala-compiler/2.13.10": { - "first": "https://javadoc.io/doc/org.scala-lang/scala-compiler/2.13.10" - }, - "org.scala-lang/scala-compiler/2.13.11": { - "first": "https://javadoc.io/doc/org.scala-lang/scala-compiler/2.13.11" - }, - "org.scala-lang/scala-compiler/2.13.12": { - "first": "https://javadoc.io/doc/org.scala-lang/scala-compiler/2.13.12" - }, - "org.scala-lang/scala-compiler/2.13.8": { - "first": "https://javadoc.io/doc/org.scala-lang/scala-compiler/2.13.8" - }, - "org.scala-lang/scala-compiler/2.13.9": { - "first": "https://javadoc.io/doc/org.scala-lang/scala-compiler/2.13.9" - }, - "org.scala-lang/scala-library/2.13.10": { - "first": "https://javadoc.io/doc/org.scala-lang/scala-library/2.13.10" - }, - "org.scala-lang/scala-library/2.13.11": { - "first": "https://javadoc.io/doc/org.scala-lang/scala-library/2.13.11" - }, - "org.scala-lang/scala-library/2.13.12": { - "first": "https://javadoc.io/doc/org.scala-lang/scala-library/2.13.12" - }, - "org.scala-lang/scala-library/2.13.8": { - "first": "https://javadoc.io/doc/org.scala-lang/scala-library/2.13.8" - }, - "org.scala-lang/scala-library/2.13.9": { - "first": "https://javadoc.io/doc/org.scala-lang/scala-library/2.13.9" - }, - "org.slf4j/slf4j-api/1.7.33": { - "first": "https://javadoc.io/doc/org.slf4j/slf4j-api/1.7.33", - "second": "https://javadoc.io/doc/org.slf4j/slf4j-api/1.7.33/package-list" - }, - "org.slf4j/slf4j-api/1.7.35": { - "first": "https://javadoc.io/doc/org.slf4j/slf4j-api/1.7.35", - "second": "https://javadoc.io/doc/org.slf4j/slf4j-api/1.7.35/package-list" - }, - "org.slf4j/slf4j-api/1.7.36": { - "first": "https://javadoc.io/doc/org.slf4j/slf4j-api/1.7.36", - "second": "https://javadoc.io/doc/org.slf4j/slf4j-api/1.7.36/package-list" - }, - "org.slf4j/slf4j-api/2.0.0": { - "first": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.0", - "second": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.0/element-list" - }, - "org.slf4j/slf4j-api/2.0.1": { - "first": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.1", - "second": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.1/element-list" - }, -<<<<<<< HEAD - "org.slf4j/slf4j-api/2.0.10": { - "first": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.10", - "second": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.10/element-list" -======= - "org.slf4j/slf4j-api/2.0.11": { - "first": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.11", - "second": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.11/element-list" - }, - "org.slf4j/slf4j-api/2.0.12": { - "first": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.12", - "second": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.12/element-list" ->>>>>>> depfixPR/master - }, - "org.slf4j/slf4j-api/2.0.2": { - "first": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.2", - "second": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.2/element-list" - }, - "org.slf4j/slf4j-api/2.0.3": { - "first": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.3", - "second": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.3/element-list" - }, - "org.slf4j/slf4j-api/2.0.4": { - "first": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.4", - "second": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.4/element-list" - }, - "org.slf4j/slf4j-api/2.0.5": { - "first": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.5", - "second": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.5/element-list" - }, - "org.slf4j/slf4j-api/2.0.6": { - "first": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.6" - }, - "org.slf4j/slf4j-api/2.0.7": { - "first": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.7", - "second": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.7/element-list" - }, - "org.slf4j/slf4j-api/2.0.9": { - "first": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.9", - "second": "https://javadoc.io/doc/org.slf4j/slf4j-api/2.0.9/element-list" - }, - "org.yaml/snakeyaml/1.30": { - "first": "https://javadoc.io/doc/org.yaml/snakeyaml/1.30", - "second": "https://javadoc.io/doc/org.yaml/snakeyaml/1.30/package-list" - }, - "org.yaml/snakeyaml/1.31": { - "first": "https://javadoc.io/doc/org.yaml/snakeyaml/1.31", - "second": "https://javadoc.io/doc/org.yaml/snakeyaml/1.31/package-list" - }, - "org.yaml/snakeyaml/1.32": { - "first": "https://javadoc.io/doc/org.yaml/snakeyaml/1.32", - "second": "https://javadoc.io/doc/org.yaml/snakeyaml/1.32/package-list" - }, - "org.yaml/snakeyaml/1.33": { - "first": "https://javadoc.io/doc/org.yaml/snakeyaml/1.33", - "second": "https://javadoc.io/doc/org.yaml/snakeyaml/1.33/package-list" - }, - "org.yaml/snakeyaml/2.0": { - "first": "https://javadoc.io/doc/org.yaml/snakeyaml/2.0" - }, - "org.yaml/snakeyaml/2.2": { - "first": "https://javadoc.io/doc/org.yaml/snakeyaml/2.2", - "second": "https://javadoc.io/doc/org.yaml/snakeyaml/2.2/element-list" - } -} \ No newline at end of file From 920dcf35821b8f44de137a0c303df6f677221e79 Mon Sep 17 00:00:00 2001 From: Tiziano Vuksan Date: Mon, 19 Feb 2024 14:38:27 +0100 Subject: [PATCH 14/22] refactor: deleted unnecessary paranthesis --- .../jsMain/kotlin/components/content/SimulationIndicators.kt | 2 +- .../jsMain/kotlin/components/content/shared/CommonProperties.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt index 61d861d911..291480711f 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt @@ -95,7 +95,7 @@ class SimulationIndicators(className: String = "") : SimplePanel(className = cla } p(className = "scale-value").bind(scaleTranslationStore) { state -> - +"Zoom: ${((state.scale/MAX_SCALE) * 100).roundDecimalPlaces(0)}%" + +"Zoom: ${(state.scale/MAX_SCALE * 100).roundDecimalPlaces(0)}%" } progress( diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt index bdcfd77d33..a88062d132 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt @@ -28,7 +28,7 @@ class CommonProperties { const val DEFAULT_START_POSITION = 0 const val DEFAULT_SCALE_RATIO = 0.9 const val MIN_SCALE = 1.0 - const val MAX_SCALE = MIN_SCALE * ((1 / DEFAULT_SCALE_RATIO) * SCALE_ITERATIONS + 1) + const val MAX_SCALE = MIN_SCALE * (1 / DEFAULT_SCALE_RATIO * SCALE_ITERATIONS) const val DEFAULT_NODE_COLOR = "#0F4AA2" } From 6f9e2a7d8b1bbacbe6d47a3b17ebe5ce0ae58556 Mon Sep 17 00:00:00 2001 From: Tiziano Vuksan Date: Thu, 29 Feb 2024 11:40:41 +0100 Subject: [PATCH 15/22] refactor: changed file names Added subscription if simulation is auto-started --- alchemist-webgui/build.gradle.kts | 1 + .../src/jsMain/kotlin/components/Content.kt | 4 ++-- ...SimulationIndicators.kt => ContextIndicators.kt} | 2 +- .../kotlin/components/content/NodeProperties.kt | 4 ++-- .../kotlin/components/content/SimulationContext.kt | 13 +++++++++++++ 5 files changed, 19 insertions(+), 5 deletions(-) rename alchemist-webgui/src/jsMain/kotlin/components/content/{SimulationIndicators.kt => ContextIndicators.kt} (97%) diff --git a/alchemist-webgui/build.gradle.kts b/alchemist-webgui/build.gradle.kts index 9d3ffb3d7d..80a4525077 100644 --- a/alchemist-webgui/build.gradle.kts +++ b/alchemist-webgui/build.gradle.kts @@ -100,6 +100,7 @@ kotlin { dependencies { implementation(incarnation("sapere")) implementation(incarnation("protelis")) + implementation(alchemist("api")) implementation(alchemist("cognitive-agents")) implementation("io.ktor:ktor-server-html-builder:2.3.8") implementation(libs.bundles.ktor.server) diff --git a/alchemist-webgui/src/jsMain/kotlin/components/Content.kt b/alchemist-webgui/src/jsMain/kotlin/components/Content.kt index a33e09b514..26696de062 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/Content.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/Content.kt @@ -9,9 +9,9 @@ package components +import components.content.ContextIndicators import components.content.NodeProperties import components.content.SimulationContext -import components.content.SimulationIndicators import io.kvision.core.AlignItems import io.kvision.core.Background import io.kvision.core.Col @@ -60,7 +60,7 @@ class Content(className: String = "") : SimplePanel(className = className) { height = 95.perc marginTop = 12.px add( - SimulationIndicators(className = "simulation-indicators").apply { + ContextIndicators(className = "context-indicators").apply { width = 100.perc height = 100.perc }, diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/ContextIndicators.kt similarity index 97% rename from alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt rename to alchemist-webgui/src/jsMain/kotlin/components/content/ContextIndicators.kt index 291480711f..8feaf35442 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationIndicators.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/ContextIndicators.kt @@ -43,7 +43,7 @@ import korlibs.math.roundDecimalPlaces * * @param className the CSS class name for styling the panel (optional) */ -class SimulationIndicators(className: String = "") : SimplePanel(className = className) { +class ContextIndicators(className: String = "") : SimplePanel(className = className) { init { boxShadow = BoxShadow(0.px, 0.px, 5.px, 0.px, Color.rgba(0, 0, 0, (0.4 * 255).toInt())) diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/NodeProperties.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/NodeProperties.kt index a0ddca56ff..c702b99f89 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/NodeProperties.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/NodeProperties.kt @@ -66,8 +66,8 @@ class NodeProperties(className: String = "") : SimplePanel(className = className hPanel( FlexWrap.NOWRAP, - JustifyContent.CENTER, - AlignItems.START, + JustifyContent.SPACEEVENLY, + AlignItems.CENTER, spacing = 15, className = "nodeinfo-header", ) { diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt index 21a1575bab..1911ea1b62 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt @@ -32,10 +32,15 @@ import io.kvision.panel.flexPanel import io.kvision.state.bind import io.kvision.utils.perc import io.kvision.utils.px +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.w3c.dom.CanvasRenderingContext2D import org.w3c.dom.DOMRect import stores.EnvironmentStore +import stores.SimulationStatus import stores.actions.ScaleTranslateAction +import utils.SimState /** * Class representing the simulation context in the application. @@ -66,6 +71,14 @@ class SimulationContext(className: String = "") : SimplePanel(className = classN } } + SimulationStatus.simulationStore.subscribe { sim -> + if (SimState.toSimStatus(sim.status?.simulationStatus) == SimState.RUNNING) { + CoroutineScope(Dispatchers.Default).launch { + EnvironmentStore.callEnvironmentSubscription() + } + } + } + flexPanel( FlexDirection.COLUMN, FlexWrap.NOWRAP, From e152f81e04edfb60371c3f6acc79ac9c02f31a9c Mon Sep 17 00:00:00 2001 From: Tiziano Vuksan Date: Thu, 29 Feb 2024 12:05:04 +0100 Subject: [PATCH 16/22] fix: added task compilation dependencies --- alchemist-webgui/build.gradle.kts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/alchemist-webgui/build.gradle.kts b/alchemist-webgui/build.gradle.kts index 80a4525077..3813eb9d51 100644 --- a/alchemist-webgui/build.gradle.kts +++ b/alchemist-webgui/build.gradle.kts @@ -144,3 +144,7 @@ tasks.named("runKtlintFormatOverCommonMainSourceSet").configure { tasks.named("runKtlintCheckOverCommonMainSourceSet").configure { dependsOn(tasks.named("kspCommonMainKotlinMetadata")) } + +tasks.named("sourcesJar").configure { + dependsOn("kspCommonMainKotlinMetadata") +} From b68920c85ca61fe581305af2cefae166b8250266 Mon Sep 17 00:00:00 2001 From: Tiziano Vuksan Date: Wed, 6 Mar 2024 19:34:19 +0100 Subject: [PATCH 17/22] chore: disabled failing graphql task --- alchemist-webgui/build.gradle.kts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/alchemist-webgui/build.gradle.kts b/alchemist-webgui/build.gradle.kts index 3813eb9d51..a58777bdae 100644 --- a/alchemist-webgui/build.gradle.kts +++ b/alchemist-webgui/build.gradle.kts @@ -1,5 +1,6 @@ import Libs.alchemist import Libs.incarnation +import com.expediagroup.graphql.plugin.gradle.tasks.AbstractGenerateClientTask import io.gitlab.arturbosch.detekt.Detekt import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig @@ -148,3 +149,9 @@ tasks.named("runKtlintCheckOverCommonMainSourceSet").configure { tasks.named("sourcesJar").configure { dependsOn("kspCommonMainKotlinMetadata") } + +tasks { + withType().configureEach { + enabled = false + } +} From f488dd50434b6f74afc243fa62ad087e4219dacf Mon Sep 17 00:00:00 2001 From: Tiziano Vuksan Date: Wed, 6 Mar 2024 20:41:07 +0100 Subject: [PATCH 18/22] docs: added missing docs and fixed style warnings --- alchemist-webgui/src/jsMain/kotlin/Client.kt | 2 +- .../src/jsMain/kotlin/components/Appbar.kt | 10 +++- .../components/content/ContextIndicators.kt | 6 +- .../components/content/NodeProperties.kt | 9 ++- .../components/content/SimulationContext.kt | 9 ++- .../content/shared/CommonProperties.kt | 57 ++++++++++++++++++- .../jsMain/kotlin/stores/EnvironmentStore.kt | 7 ++- .../stores/reducers/ScaleTranslateReducer.kt | 14 +++-- .../stores/states/ScaleTranslateState.kt | 6 +- .../kotlin/stores/states/SimulationState.kt | 3 +- .../src/jsMain/kotlin/utils/RenderingUtils.kt | 13 ++++- 11 files changed, 117 insertions(+), 19 deletions(-) diff --git a/alchemist-webgui/src/jsMain/kotlin/Client.kt b/alchemist-webgui/src/jsMain/kotlin/Client.kt index d34d18692f..cbb13bc107 100644 --- a/alchemist-webgui/src/jsMain/kotlin/Client.kt +++ b/alchemist-webgui/src/jsMain/kotlin/Client.kt @@ -20,7 +20,7 @@ import io.kvision.startApplication import ui.AppMain /** - * The application main entry point + * The application main entry point. * * */ fun main() { diff --git a/alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt b/alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt index 11dfff10d6..afb5b0280f 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/Appbar.kt @@ -24,6 +24,10 @@ import io.kvision.panel.flexPanel import io.kvision.state.bind import stores.SimulationStatus +/** + * Spacing between elements in the flex panel. + */ + /** * Class representing the application's top navigation bar. * @@ -33,6 +37,10 @@ class Appbar(className: String = "") : SimplePanel(className = className) { private val simulationFileName = "" + private object StyleParameters { + const val SPACING = 15 + } + init { navbar( if (simulationFileName.isEmpty()) { @@ -49,7 +57,7 @@ class Appbar(className: String = "") : SimplePanel(className = className) { flexPanel { flexDirection = FlexDirection.ROW alignItems = AlignItems.CENTER - spacing = 15 + spacing = StyleParameters.SPACING div { color = Color.name(Col.WHITE) }.bind(SimulationStatus.simulationStore) { sim -> diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/ContextIndicators.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/ContextIndicators.kt index 8feaf35442..5d6751912e 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/ContextIndicators.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/ContextIndicators.kt @@ -45,8 +45,12 @@ import korlibs.math.roundDecimalPlaces */ class ContextIndicators(className: String = "") : SimplePanel(className = className) { + private object StyleParameters { + const val ALPHA_VALUE = (0.4 * 255).toInt() + } + init { - boxShadow = BoxShadow(0.px, 0.px, 5.px, 0.px, Color.rgba(0, 0, 0, (0.4 * 255).toInt())) + boxShadow = BoxShadow(0.px, 0.px, 5.px, 0.px, Color.rgba(0, 0, 0, StyleParameters.ALPHA_VALUE)) background = Background(Color.name(Col.WHITE)) borderRadius = 10.px flexPanel( diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/NodeProperties.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/NodeProperties.kt index c702b99f89..a83340509b 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/NodeProperties.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/NodeProperties.kt @@ -42,9 +42,13 @@ import stores.NodeStore */ class NodeProperties(className: String = "") : SimplePanel(className = className) { + private object StyleParameters { + const val ALPHA_VALUE = (0.4 * 255).toInt() + } + init { borderRadius = CssSize(10, UNIT.px) - boxShadow = BoxShadow(0.px, 0.px, 5.px, 0.px, Color.rgba(0, 0, 0, (0.4 * 255).toInt())) + boxShadow = BoxShadow(0.px, 0.px, 5.px, 0.px, Color.rgba(0, 0, 0, StyleParameters.ALPHA_VALUE)) background = Background(color = Color.name(Col.WHITE)) flexPanel( @@ -97,7 +101,8 @@ class NodeProperties(className: String = "") : SimplePanel(className = className width = 100.perc overflowWrap = OverflowWrap.ANYWHERE }.bind(NodeStore.nodeStore) { - +"X: ${it.node?.nodePosition?.coordinates?.get(0)}, Y:${it.node?.nodePosition?.coordinates?.get(1)}" + val coords = it.node?.nodePosition?.coordinates + +"X: ${coords?.get(0)}, Y:${coords?.get(1)}" } } } diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt index 1911ea1b62..ed23f723bf 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/SimulationContext.kt @@ -60,6 +60,10 @@ class SimulationContext(className: String = "") : SimplePanel(className = classN RenderProperties.DEFAULT_START_POSITION.toDouble(), ) + private object StyleParameters { + const val ALPHA_VALUE = (0.4 * 255).toInt() + } + init { EnvironmentStore.callEnvironmentQuery() @@ -95,7 +99,10 @@ class SimulationContext(className: String = "") : SimplePanel(className = classN canvasWidth = RenderProperties.DEFAULT_WIDTH.toInt() canvasHeight = RenderProperties.DEFAULT_HEIGHT.toInt() borderRadius = CssSize(10, UNIT.px) - boxShadow = BoxShadow(0.px, 0.px, 5.px, 0.px, Color.rgba(0, 0, 0, (0.4 * 255).toInt())) + boxShadow = BoxShadow( + 0.px, 0.px, 5.px, 0.px, + Color.rgba(0, 0, 0, StyleParameters.ALPHA_VALUE), + ) background = Background(color = Color.name(Col.WHITE)) addAfterInsertHook { diff --git a/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt b/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt index a88062d132..237886b29c 100644 --- a/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt +++ b/alchemist-webgui/src/jsMain/kotlin/components/content/shared/CommonProperties.kt @@ -14,32 +14,85 @@ import io.kvision.state.ObservableValue import stores.reducers.scaleTranslateReducer import stores.states.ScaleTranslateState +/** + * Class containing common properties to the whole UI. + * + * */ class CommonProperties { /** * Object containing properties related to the canvas context. */ object RenderProperties { + /** + * Number of iterations for scaling calculations. + */ private const val SCALE_ITERATIONS = 100 + + /** + * Default radius for nodes. + */ const val DEFUALT_NODE_RADIUS = 10.0 + + /** + * Default height of the canvas. + */ const val DEFAULT_HEIGHT = 875.0 + + /** + * Default width of the canvas. + */ const val DEFAULT_WIDTH = 1350.0 // 1375.0 - const val DEFAULT_SCALE = 1.0 + + /** + * Default scale factor for rendering. + */ + const val DEFAULT_SCALE = 13.0 + + /** + * Default starting position for rendering. + */ const val DEFAULT_START_POSITION = 0 + + /** + * Default ratio for scaling. + */ const val DEFAULT_SCALE_RATIO = 0.9 + + /** + * Minimum scale allowed. + */ const val MIN_SCALE = 1.0 + + /** + * Maximum scale allowed. + */ const val MAX_SCALE = MIN_SCALE * (1 / DEFAULT_SCALE_RATIO * SCALE_ITERATIONS) - const val DEFAULT_NODE_COLOR = "#0F4AA2" + + /** + * Default color for nodes. + */ + const val DEFAULT_NODE_COLOR = "#0E835C" // "#0F4AA2" } /** * Object containing observable properties. */ object Observables { + /** + * Redux store for managing scale and translation. + */ val scaleTranslationStore = createTypedReduxStore(::scaleTranslateReducer, ScaleTranslateState()) + + /** + * Observable value for node radius. + */ var nodesRadius: ObservableValue = ObservableValue(RenderProperties.DEFUALT_NODE_RADIUS) } + /** + * Utility methods. + */ object Utils { /** diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt b/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt index 28a46a35f2..a99117b682 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/EnvironmentStore.kt @@ -58,7 +58,12 @@ object EnvironmentStore { ?.environment ?.nodeToPos ?.entries!! - .map { e -> EnvironmentSubscription.Entry(e.id, EnvironmentSubscription.Position(e.position.coordinates)) } + .map { e -> + EnvironmentSubscription.Entry( + e.id, + EnvironmentSubscription.Position(e.position.coordinates), + ) + } store.dispatch(EnvironmentStateAction.AddAllNodes(result)) } } diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/reducers/ScaleTranslateReducer.kt b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/ScaleTranslateReducer.kt index e7d713ebfe..54ad5dafcc 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/reducers/ScaleTranslateReducer.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/reducers/ScaleTranslateReducer.kt @@ -18,12 +18,14 @@ import stores.states.ScaleTranslateState * @param action The action to apply to the state. * @return The new state of scale and translation after applying the action. */ -fun scaleTranslateReducer(state: ScaleTranslateState, action: ScaleTranslateAction): ScaleTranslateState = when (action) { - is ScaleTranslateAction.SetScale -> { - state.copy(scale = action.scale) - } +fun scaleTranslateReducer(state: ScaleTranslateState, action: ScaleTranslateAction): ScaleTranslateState { + return when (action) { + is ScaleTranslateAction.SetScale -> { + state.copy(scale = action.scale) + } - is ScaleTranslateAction.SetTranslation -> { - state.copy(translate = action.translate) + is ScaleTranslateAction.SetTranslation -> { + state.copy(translate = action.translate) + } } } diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/states/ScaleTranslateState.kt b/alchemist-webgui/src/jsMain/kotlin/stores/states/ScaleTranslateState.kt index fe90a7f7ca..b6f589b292 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/states/ScaleTranslateState.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/states/ScaleTranslateState.kt @@ -14,8 +14,10 @@ import components.content.shared.CommonProperties.RenderProperties.DEFAULT_SCALE /** * Represents the state of scaling and translation in the canvas context. * @property scale The scaling factor applied to the graphical elements. Defaults to DEFAULT_SCALE. - * @property translate The translation offset applied to the graphical elements, represented as a pair of (x, y) coordinates. Defaults to (0.0, 0.0). - * @constructor Creates a ScaleTranslateState with the specified scaling factor and translation offset, which default to DEFAULT_SCALE and (0.0, 0.0) respectively. + * @property translate The translation offset applied to the graphical elements, represented as a pair of (x, y) + * coordinates. Defaults to (0.0, 0.0). + * @constructor Creates a ScaleTranslateState with the specified scaling factor and translation offset, which default + * to DEFAULT_SCALE and (0.0, 0.0) respectively. */ data class ScaleTranslateState( var scale: Double = DEFAULT_SCALE, diff --git a/alchemist-webgui/src/jsMain/kotlin/stores/states/SimulationState.kt b/alchemist-webgui/src/jsMain/kotlin/stores/states/SimulationState.kt index e8561fff0b..3f20d33207 100644 --- a/alchemist-webgui/src/jsMain/kotlin/stores/states/SimulationState.kt +++ b/alchemist-webgui/src/jsMain/kotlin/stores/states/SimulationState.kt @@ -13,7 +13,8 @@ import it.unibo.alchemist.boundary.graphql.client.SimulationStatusQuery /** * Represents the state of a simulation, including its status data. - * @property status The status data of the simulation, represented by a SimulationStatusQuery.Data object. Defaults to null. + * @property status The status data of the simulation, represented by a SimulationStatusQuery.Data object. + * Defaults to null. * @constructor Creates a SimulationState with the specified status data, which defaults to null. */ data class SimulationState(val status: SimulationStatusQuery.Data? = null) diff --git a/alchemist-webgui/src/jsMain/kotlin/utils/RenderingUtils.kt b/alchemist-webgui/src/jsMain/kotlin/utils/RenderingUtils.kt index 265525f383..7192c43f2f 100644 --- a/alchemist-webgui/src/jsMain/kotlin/utils/RenderingUtils.kt +++ b/alchemist-webgui/src/jsMain/kotlin/utils/RenderingUtils.kt @@ -11,6 +11,16 @@ package utils import kotlin.random.Random +/** + * Length of the generated color string. + */ +const val COLOR_LENGTH = 6 + +/** + * Maximum index for selecting characters from the letters string. + */ +const val MAX_INDEX = 15 + /** * Checks if the mouse pointer is over a node position within the given radius. * @@ -21,6 +31,7 @@ import kotlin.random.Random * @param radius The radius of the node. * @return True if the mouse pointer is over the node position, false otherwise. */ + fun isMouseOverNodePosition(mouseX: Double, mouseY: Double, nodeX: Double, nodeY: Double, radius: Double): Boolean { val dx = mouseX - nodeX val dy = mouseY - nodeY @@ -36,6 +47,6 @@ fun isMouseOverNodePosition(mouseX: Double, mouseY: Double, nodeX: Double, nodeY fun randomColor(): String { val letters = "0123456789ABCDEF" var color = "#" - repeat(6) { color += letters[Random.nextInt(15)] } + repeat(COLOR_LENGTH) { color += letters[Random.nextInt(MAX_INDEX)] } return color } From ae5f53cd65dc3d644cb231d02f1ec2bfa8b33631 Mon Sep 17 00:00:00 2001 From: Tiziano Vuksan Date: Wed, 6 Mar 2024 21:27:17 +0100 Subject: [PATCH 19/22] fix: fixed dependency injection warning --- .../unibo/alchemist/boundary/webgui/monitor/WebUIMonitor.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/webgui/monitor/WebUIMonitor.kt b/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/webgui/monitor/WebUIMonitor.kt index 05b3d93598..c70f6aaac6 100644 --- a/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/webgui/monitor/WebUIMonitor.kt +++ b/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/webgui/monitor/WebUIMonitor.kt @@ -30,11 +30,13 @@ import it.unibo.alchemist.boundary.launchers.GraphQLServer as GraphQLServer * @param T The type of elements in the environment. * @param P The type of positions associated with elements in the environment. * @param environment The environment to monitor using the web UI. + * @param serverDispatcher The coroutine dispatcher for server operations. */ class WebUIMonitor> ( environment: Environment, + private val serverDispatcher: CoroutineDispatcher = Dispatchers.Default, ) : GraphQLServer(environment) { - private val serverDispatcher: CoroutineDispatcher = Dispatchers.Default + private lateinit var webServer: ApplicationEngine override fun initialized(environment: Environment) { From a6a6e912939f48b9823a829882810c31efca4417 Mon Sep 17 00:00:00 2001 From: Tiziano Vuksan Date: Thu, 7 Mar 2024 14:57:26 +0100 Subject: [PATCH 20/22] fix: fixed jvm constructor overload --- .../it/unibo/alchemist/boundary/webgui/monitor/WebUIMonitor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/webgui/monitor/WebUIMonitor.kt b/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/webgui/monitor/WebUIMonitor.kt index c70f6aaac6..bb2e5a7bc2 100644 --- a/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/webgui/monitor/WebUIMonitor.kt +++ b/alchemist-webgui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/webgui/monitor/WebUIMonitor.kt @@ -32,7 +32,7 @@ import it.unibo.alchemist.boundary.launchers.GraphQLServer as GraphQLServer * @param environment The environment to monitor using the web UI. * @param serverDispatcher The coroutine dispatcher for server operations. */ -class WebUIMonitor> ( +class WebUIMonitor> @JvmOverloads constructor( environment: Environment, private val serverDispatcher: CoroutineDispatcher = Dispatchers.Default, ) : GraphQLServer(environment) { From a95efc1e687352ace7eb42e1748a14f7362ef6db Mon Sep 17 00:00:00 2001 From: AngeloFilaseta Date: Mon, 11 Mar 2024 14:15:51 +0100 Subject: [PATCH 21/22] fix(build): solve task dependencies in webgui --- alchemist-web-renderer/build.gradle.kts | 5 ++ alchemist-webgui/build.gradle.kts | 57 ++++++++----------- .../kotlin/graphql/api/EnvironmentApi.kt | 3 + .../graphql/api/SimulationControlApi.kt | 3 + .../src/commonMain/kotlin/utils/SimState.kt | 3 +- 5 files changed, 38 insertions(+), 33 deletions(-) diff --git a/alchemist-web-renderer/build.gradle.kts b/alchemist-web-renderer/build.gradle.kts index 29169577b4..dae75b6b6f 100644 --- a/alchemist-web-renderer/build.gradle.kts +++ b/alchemist-web-renderer/build.gradle.kts @@ -22,6 +22,11 @@ kotlin { js(IR) { browser { binaries.executable() + testTask { + useKarma { + useChromeHeadless() + } + } } } sourceSets { diff --git a/alchemist-webgui/build.gradle.kts b/alchemist-webgui/build.gradle.kts index a58777bdae..282f73bbff 100644 --- a/alchemist-webgui/build.gradle.kts +++ b/alchemist-webgui/build.gradle.kts @@ -14,7 +14,6 @@ plugins { alias(libs.plugins.kotlin.serialization) alias(libs.plugins.graphql.server) alias(libs.plugins.graphql.client) - id("io.kvision") version "7.3.1" } @@ -52,36 +51,27 @@ kotlin { } js(IR) { browser { - runTask( - Action { - mainOutputFileName = "alchemist-webgui.js" - sourceMaps = false - devServer = KotlinWebpackConfig.DevServer( - open = false, - port = 3000, - proxy = mutableMapOf( - "/kv/*" to "http://localhost:8080", - "/kvws/*" to mapOf("target" to "ws://localhost:8080", "ws" to true), - ), - static = mutableListOf("${layout.buildDirectory.asFile.get()}/processedResources/js/main"), - ) - }, - ) - - webpackTask( - Action { - mainOutputFileName = "alchemist-webgui.js" - }, - ) - - testTask( - Action { - useKarma { - useChromeHeadless() - } - }, - ) - + runTask { + mainOutputFileName = "alchemist-webgui.js" + sourceMaps = false + devServer = KotlinWebpackConfig.DevServer( + open = false, + port = 3000, + proxy = mutableMapOf( + "/kv/*" to "http://localhost:8080", + "/kvws/*" to mapOf("target" to "ws://localhost:8080", "ws" to true), + ), + static = mutableListOf("${layout.buildDirectory.asFile.get()}/processedResources/js/main"), + ) + } + webpackTask { + mainOutputFileName = "alchemist-webgui.js" + } + testTask { + useKarma { + useChromeHeadless() + } + } binaries.executable() } } @@ -116,7 +106,6 @@ kotlin { implementation(libs.apollo.runtime) implementation(libs.bundles.ktor.client) implementation(libs.bundles.kotlin.react) - implementation("io.kvision:kvision:$kvisionVersion") implementation("io.kvision:kvision-bootstrap:$kvisionVersion") implementation("io.kvision:kvision-richtext:$kvisionVersion") @@ -150,6 +139,10 @@ tasks.named("sourcesJar").configure { dependsOn("kspCommonMainKotlinMetadata") } +tasks.withType().configureEach { + dependsOn("kspCommonMainKotlinMetadata") +} + tasks { withType().configureEach { enabled = false diff --git a/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt b/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt index 2cd71355f7..80e3cbe2a6 100644 --- a/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt +++ b/alchemist-webgui/src/commonMain/kotlin/graphql/api/EnvironmentApi.kt @@ -20,6 +20,9 @@ import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow +/** + * Provides a set of functions to interact with the environment API. + */ object EnvironmentApi { /** diff --git a/alchemist-webgui/src/commonMain/kotlin/graphql/api/SimulationControlApi.kt b/alchemist-webgui/src/commonMain/kotlin/graphql/api/SimulationControlApi.kt index e30de6544f..c8be8b0d6a 100644 --- a/alchemist-webgui/src/commonMain/kotlin/graphql/api/SimulationControlApi.kt +++ b/alchemist-webgui/src/commonMain/kotlin/graphql/api/SimulationControlApi.kt @@ -18,6 +18,9 @@ import kotlinx.coroutines.Deferred import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope +/** + * Provides a set of functions to interact with the simulation control API. + */ object SimulationControlApi { /** diff --git a/alchemist-webgui/src/commonMain/kotlin/utils/SimState.kt b/alchemist-webgui/src/commonMain/kotlin/utils/SimState.kt index ca7cbe9b65..9f9c153caa 100644 --- a/alchemist-webgui/src/commonMain/kotlin/utils/SimState.kt +++ b/alchemist-webgui/src/commonMain/kotlin/utils/SimState.kt @@ -46,7 +46,8 @@ enum class SimState { * Converts a string representation of a simulation status to the corresponding SimState enum value. * * @param value the string representation of the simulation status - * @return the SimState enum value corresponding to the input string, or SimState.TERMINATED if the input is invalid + * @return the SimState enum value corresponding to the input string, or [SimState.TERMINATED] if the input is + * invalid. */ fun toSimStatus(value: String?): SimState { return when (value) { From 6231e0a5b25498ad78d20ac80a7612ed0c0d9198 Mon Sep 17 00:00:00 2001 From: AngeloFilaseta Date: Mon, 11 Mar 2024 15:42:53 +0100 Subject: [PATCH 22/22] refactor(build): better organization --- alchemist-webgui/build.gradle.kts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/alchemist-webgui/build.gradle.kts b/alchemist-webgui/build.gradle.kts index 282f73bbff..47c7f1b8b4 100644 --- a/alchemist-webgui/build.gradle.kts +++ b/alchemist-webgui/build.gradle.kts @@ -127,12 +127,10 @@ fun PatternFilterable.excludeGenerated() = exclude { "build${File.separator}gene tasks.withType().configureEach { excludeGenerated() } ktlint { filter { excludeGenerated() } } -tasks.named("runKtlintFormatOverCommonMainSourceSet").configure { - dependsOn(tasks.named("kspCommonMainKotlinMetadata")) -} - -tasks.named("runKtlintCheckOverCommonMainSourceSet").configure { - dependsOn(tasks.named("kspCommonMainKotlinMetadata")) +listOf("Check", "Format").forEach { + tasks.named("runKtlint${it}OverCommonMainSourceSet").configure { + dependsOn(tasks.named("kspCommonMainKotlinMetadata")) + } } tasks.named("sourcesJar").configure {