diff --git a/package.json b/package.json index 2a7fa86e..1691c225 100644 --- a/package.json +++ b/package.json @@ -20,69 +20,73 @@ "homepage": "https://meshtastic.org", "dependencies": { "@emeraldpay/hashicon-react": "^0.5.2", - "@headlessui/react": "^1.7.8", - "@heroicons/react": "^2.0.14", "@hookform/error-message": "^2.0.1", - "@hookform/resolvers": "^2.9.10", - "@meshtastic/meshtasticjs": "2.0.15-0", - "@primer/octicons-react": "^17.11.1", + "@hookform/resolvers": "^2.9.11", + "@meshtastic/meshtasticjs": "2.0.20-1", + "@radix-ui/react-dialog": "^1.0.2", + "@radix-ui/react-label": "^2.0.0", + "@radix-ui/react-menubar": "^1.0.0", + "@radix-ui/react-popover": "^1.0.3", + "@radix-ui/react-select": "^1.2.0", + "@radix-ui/react-separator": "^1.0.1", + "@radix-ui/react-switch": "^1.0.1", + "@radix-ui/react-tabs": "^1.0.2", + "@radix-ui/react-toast": "^1.1.2", + "@radix-ui/react-tooltip": "^1.0.3", "@tailwindcss/typography": "^0.5.9", "@turf/turf": "^6.5.0", "base64-js": "^1.5.1", - "chart.js": "^4.2.0", - "chartjs-adapter-date-fns": "^3.0.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", - "date-fns": "^2.29.3", + "class-variance-authority": "^0.4.0", + "clsx": "^1.2.1", + "cmdk": "^0.1.22", "geodesy": "^2.4.0", - "i18next": "^22.4.9", "immer": "^9.0.19", + "lucide-react": "^0.112.0", "mapbox-gl": "npm:empty-npm-package@^1.0.0", "maplibre-gl": "2.4.0", - "pretty-ms": "^8.0.0", "react": "^18.2.0", - "react-chartjs-2": "^5.2.0", "react-dom": "^18.2.0", - "react-hook-form": "^7.42.1", - "react-hot-toast": "^2.4.0", - "react-i18next": "^12.1.4", - "react-json-tree": "^0.18.0", + "react-hook-form": "^7.43.1", "react-map-gl": "^7.0.21", "react-qrcode-logo": "^2.8.0", "rfc4648": "^1.5.2", + "tailwind-merge": "^1.9.1", + "tailwindcss-animate": "^1.0.5", "timeago-react": "^3.0.5", - "zustand": "4.3.2" + "zustand": "4.3.3" }, "devDependencies": { "@tailwindcss/forms": "^0.5.3", - "@types/chrome": "^0.0.210", + "@types/chrome": "^0.0.212", "@types/geodesy": "^2.2.3", - "@types/node": "^18.11.18", - "@types/react": "^18.0.27", + "@types/node": "^18.13.0", + "@types/react": "^18.0.28", "@types/react-dom": "^18.0.10", "@types/w3c-web-serial": "^1.0.3", "@types/web-bluetooth": "^0.0.16", - "@typescript-eslint/eslint-plugin": "^5.49.0", - "@typescript-eslint/parser": "^5.49.0", - "@vitejs/plugin-react": "^3.0.1", + "@typescript-eslint/eslint-plugin": "^5.51.0", + "@typescript-eslint/parser": "^5.51.0", + "@vitejs/plugin-react": "^3.1.0", "autoprefixer": "^10.4.13", - "eslint": "^8.33.0", + "eslint": "^8.34.0", "eslint-config-prettier": "^8.6.0", "eslint-import-resolver-typescript": "^3.5.3", "eslint-plugin-import": "^2.27.5", - "eslint-plugin-react": "^7.32.1", + "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", "gzipper": "^7.2.0", "postcss": "^8.4.21", - "prettier": "^2.8.3", + "prettier": "^2.8.4", "prettier-plugin-tailwindcss": "^0.2.2", "rollup-plugin-visualizer": "^5.9.0", - "tailwindcss": "^3.2.4", + "tailwindcss": "^3.2.6", "tar": "^6.1.13", "tslib": "^2.5.0", - "typescript": "^4.9.4", - "vite": "^4.0.4", + "typescript": "^4.9.5", + "vite": "^4.1.1", "vite-plugin-environment": "^1.1.3", - "vite-plugin-pwa": "^0.14.1" + "vite-plugin-pwa": "^0.14.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f186f13a..942c5d42 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2,135 +2,143 @@ lockfileVersion: 5.4 specifiers: '@emeraldpay/hashicon-react': ^0.5.2 - '@headlessui/react': ^1.7.8 - '@heroicons/react': ^2.0.14 '@hookform/error-message': ^2.0.1 - '@hookform/resolvers': ^2.9.10 - '@meshtastic/meshtasticjs': 2.0.15-0 - '@primer/octicons-react': ^17.11.1 + '@hookform/resolvers': ^2.9.11 + '@meshtastic/meshtasticjs': 2.0.20-1 + '@radix-ui/react-dialog': ^1.0.2 + '@radix-ui/react-label': ^2.0.0 + '@radix-ui/react-menubar': ^1.0.0 + '@radix-ui/react-popover': ^1.0.3 + '@radix-ui/react-select': ^1.2.0 + '@radix-ui/react-separator': ^1.0.1 + '@radix-ui/react-switch': ^1.0.1 + '@radix-ui/react-tabs': ^1.0.2 + '@radix-ui/react-toast': ^1.1.2 + '@radix-ui/react-tooltip': ^1.0.3 '@tailwindcss/forms': ^0.5.3 '@tailwindcss/typography': ^0.5.9 '@turf/turf': ^6.5.0 - '@types/chrome': ^0.0.210 + '@types/chrome': ^0.0.212 '@types/geodesy': ^2.2.3 - '@types/node': ^18.11.18 - '@types/react': ^18.0.27 + '@types/node': ^18.13.0 + '@types/react': ^18.0.28 '@types/react-dom': ^18.0.10 '@types/w3c-web-serial': ^1.0.3 '@types/web-bluetooth': ^0.0.16 - '@typescript-eslint/eslint-plugin': ^5.49.0 - '@typescript-eslint/parser': ^5.49.0 - '@vitejs/plugin-react': ^3.0.1 + '@typescript-eslint/eslint-plugin': ^5.51.0 + '@typescript-eslint/parser': ^5.51.0 + '@vitejs/plugin-react': ^3.1.0 autoprefixer: ^10.4.13 base64-js: ^1.5.1 - chart.js: ^4.2.0 - chartjs-adapter-date-fns: ^3.0.0 class-transformer: ^0.5.1 class-validator: ^0.14.0 - date-fns: ^2.29.3 - eslint: ^8.33.0 + class-variance-authority: ^0.4.0 + clsx: ^1.2.1 + cmdk: ^0.1.22 + eslint: ^8.34.0 eslint-config-prettier: ^8.6.0 eslint-import-resolver-typescript: ^3.5.3 eslint-plugin-import: ^2.27.5 - eslint-plugin-react: ^7.32.1 + eslint-plugin-react: ^7.32.2 eslint-plugin-react-hooks: ^4.6.0 geodesy: ^2.4.0 gzipper: ^7.2.0 - i18next: ^22.4.9 immer: ^9.0.19 + lucide-react: ^0.112.0 mapbox-gl: npm:empty-npm-package@^1.0.0 maplibre-gl: 2.4.0 postcss: ^8.4.21 - prettier: ^2.8.3 + prettier: ^2.8.4 prettier-plugin-tailwindcss: ^0.2.2 - pretty-ms: ^8.0.0 react: ^18.2.0 - react-chartjs-2: ^5.2.0 react-dom: ^18.2.0 - react-hook-form: ^7.42.1 - react-hot-toast: ^2.4.0 - react-i18next: ^12.1.4 - react-json-tree: ^0.18.0 + react-hook-form: ^7.43.1 react-map-gl: ^7.0.21 react-qrcode-logo: ^2.8.0 rfc4648: ^1.5.2 rollup-plugin-visualizer: ^5.9.0 - tailwindcss: ^3.2.4 + tailwind-merge: ^1.9.1 + tailwindcss: ^3.2.6 + tailwindcss-animate: ^1.0.5 tar: ^6.1.13 timeago-react: ^3.0.5 tslib: ^2.5.0 - typescript: ^4.9.4 - vite: ^4.0.4 + typescript: ^4.9.5 + vite: ^4.1.1 vite-plugin-environment: ^1.1.3 - vite-plugin-pwa: ^0.14.1 - zustand: 4.3.2 + vite-plugin-pwa: ^0.14.4 + zustand: 4.3.3 dependencies: '@emeraldpay/hashicon-react': 0.5.2 - '@headlessui/react': 1.7.8_biqbaboplfbrettd7655fr4n2y - '@heroicons/react': 2.0.14_react@18.2.0 - '@hookform/error-message': 2.0.1_uno7536xd2i6xn2dtvbywz2sva - '@hookform/resolvers': 2.9.10_react-hook-form@7.42.1 - '@meshtastic/meshtasticjs': 2.0.15-0 - '@primer/octicons-react': 17.11.1_react@18.2.0 - '@tailwindcss/typography': 0.5.9_tailwindcss@3.2.4 + '@hookform/error-message': 2.0.1_zf7ga3u4zrffjlingb6kh5ipva + '@hookform/resolvers': 2.9.11_react-hook-form@7.43.1 + '@meshtastic/meshtasticjs': 2.0.20-1 + '@radix-ui/react-dialog': 1.0.2_zula6vjvt3wdocc4mwcxqa6nzi + '@radix-ui/react-label': 2.0.0_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-menubar': 1.0.0_zula6vjvt3wdocc4mwcxqa6nzi + '@radix-ui/react-popover': 1.0.3_zula6vjvt3wdocc4mwcxqa6nzi + '@radix-ui/react-select': 1.2.0_zula6vjvt3wdocc4mwcxqa6nzi + '@radix-ui/react-separator': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-switch': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-tabs': 1.0.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-toast': 1.1.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-tooltip': 1.0.3_zula6vjvt3wdocc4mwcxqa6nzi + '@tailwindcss/typography': 0.5.9_tailwindcss@3.2.6 '@turf/turf': 6.5.0 base64-js: 1.5.1 - chart.js: 4.2.0 - chartjs-adapter-date-fns: 3.0.0_n6szoxj4ax2zhp2sxsxxj6zdla class-transformer: 0.5.1 class-validator: 0.14.0 - date-fns: 2.29.3 + class-variance-authority: 0.4.0_typescript@4.9.5 + clsx: 1.2.1 + cmdk: 0.1.22_zula6vjvt3wdocc4mwcxqa6nzi geodesy: 2.4.0 - i18next: 22.4.9 immer: 9.0.19 + lucide-react: 0.112.0_react@18.2.0 mapbox-gl: /empty-npm-package/1.0.0 maplibre-gl: 2.4.0 - pretty-ms: 8.0.0 react: 18.2.0 - react-chartjs-2: 5.2.0_uxqdnqzacoapcp4mwytn5yclyu react-dom: 18.2.0_react@18.2.0 - react-hook-form: 7.42.1_react@18.2.0 - react-hot-toast: 2.4.0_biqbaboplfbrettd7655fr4n2y - react-i18next: 12.1.4_iakk3dtjhjpukdoa4oua5khgci - react-json-tree: 0.18.0_3stiutgnnbnfnf3uowm5cip22i + react-hook-form: 7.43.1_react@18.2.0 react-map-gl: 7.0.21_6eczaga5xxiwzxtfiyk6fioasq react-qrcode-logo: 2.8.0_biqbaboplfbrettd7655fr4n2y rfc4648: 1.5.2 + tailwind-merge: 1.9.1 + tailwindcss-animate: 1.0.5_tailwindcss@3.2.6 timeago-react: 3.0.5_react@18.2.0 - zustand: 4.3.2_immer@9.0.19+react@18.2.0 + zustand: 4.3.3_immer@9.0.19+react@18.2.0 devDependencies: - '@tailwindcss/forms': 0.5.3_tailwindcss@3.2.4 - '@types/chrome': 0.0.210 + '@tailwindcss/forms': 0.5.3_tailwindcss@3.2.6 + '@types/chrome': 0.0.212 '@types/geodesy': 2.2.3 - '@types/node': 18.11.18 - '@types/react': 18.0.27 + '@types/node': 18.13.0 + '@types/react': 18.0.28 '@types/react-dom': 18.0.10 '@types/w3c-web-serial': 1.0.3 '@types/web-bluetooth': 0.0.16 - '@typescript-eslint/eslint-plugin': 5.49.0_rsaczafy73x3xqauzesvzbsgzy - '@typescript-eslint/parser': 5.49.0_zkdaqh7it7uc4cvz2haft7rc6u - '@vitejs/plugin-react': 3.0.1_vite@4.0.4 + '@typescript-eslint/eslint-plugin': 5.51.0_z4swst3wuuqk4hlme4ajzslgh4 + '@typescript-eslint/parser': 5.51.0_7kw3g6rralp5ps6mg3uyzz6azm + '@vitejs/plugin-react': 3.1.0_vite@4.1.1 autoprefixer: 10.4.13_postcss@8.4.21 - eslint: 8.33.0 - eslint-config-prettier: 8.6.0_eslint@8.33.0 - eslint-import-resolver-typescript: 3.5.3_ohdts44xlqyeyrlje4qnefqeay - eslint-plugin-import: 2.27.5_c6pjvta7aysubsdnhvgec47vxe - eslint-plugin-react: 7.32.1_eslint@8.33.0 - eslint-plugin-react-hooks: 4.6.0_eslint@8.33.0 + eslint: 8.34.0 + eslint-config-prettier: 8.6.0_eslint@8.34.0 + eslint-import-resolver-typescript: 3.5.3_mvgyw3chnqkp6sgfmmtihyjpnm + eslint-plugin-import: 2.27.5_inmo4nrctlhmfx73hzu6aogupa + eslint-plugin-react: 7.32.2_eslint@8.34.0 + eslint-plugin-react-hooks: 4.6.0_eslint@8.34.0 gzipper: 7.2.0 postcss: 8.4.21 - prettier: 2.8.3 - prettier-plugin-tailwindcss: 0.2.2_prettier@2.8.3 + prettier: 2.8.4 + prettier-plugin-tailwindcss: 0.2.2_prettier@2.8.4 rollup-plugin-visualizer: 5.9.0 - tailwindcss: 3.2.4_postcss@8.4.21 + tailwindcss: 3.2.6_postcss@8.4.21 tar: 6.1.13 tslib: 2.5.0 - typescript: 4.9.4 - vite: 4.0.4_@types+node@18.11.18 - vite-plugin-environment: 1.1.3_vite@4.0.4 - vite-plugin-pwa: 0.14.1_vite@4.0.4 + typescript: 4.9.5 + vite: 4.1.1_@types+node@18.13.0 + vite-plugin-environment: 1.1.3_vite@4.1.1 + vite-plugin-pwa: 0.14.4_vite@4.1.1 packages: @@ -176,7 +184,7 @@ packages: '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.20.12 '@babel/helper-module-transforms': 7.20.11 '@babel/helpers': 7.20.13 - '@babel/parser': 7.20.13 + '@babel/parser': 7.20.15 '@babel/template': 7.20.7 '@babel/traverse': 7.20.13 '@babel/types': 7.20.7 @@ -222,7 +230,7 @@ packages: '@babel/compat-data': 7.20.14 '@babel/core': 7.20.12 '@babel/helper-validator-option': 7.18.6 - browserslist: 4.21.4 + browserslist: 4.21.5 lru-cache: 5.1.1 semver: 6.3.0 dev: true @@ -254,7 +262,7 @@ packages: dependencies: '@babel/core': 7.20.12 '@babel/helper-annotate-as-pure': 7.18.6 - regexpu-core: 5.2.2 + regexpu-core: 5.3.0 dev: true /@babel/helper-define-polyfill-provider/0.3.3_@babel+core@7.20.12: @@ -439,8 +447,8 @@ packages: js-tokens: 4.0.0 dev: true - /@babel/parser/7.20.13: - resolution: {integrity: sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==} + /@babel/parser/7.20.15: + resolution: {integrity: sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg==} engines: {node: '>=6.0.0'} hasBin: true dependencies: @@ -826,8 +834,8 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-block-scoping/7.20.14_@babel+core@7.20.12: - resolution: {integrity: sha512-sMPepQtsOs5fM1bwNvuJJHvaCfOEQfmc01FGw0ELlTpTJj5Ql/zuNRRldYhAPys4ghXdBIQJbRVYi44/7QflQQ==} + /@babel/plugin-transform-block-scoping/7.20.15_@babel+core@7.20.12: + resolution: {integrity: sha512-Vv4DMZ6MiNOhu/LdaZsT/bsLRxgL94d269Mv4R/9sp6+Mp++X/JqypZYypJXLlM4mlL352/Egzbzr98iABH1CA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1219,7 +1227,7 @@ packages: '@babel/plugin-transform-arrow-functions': 7.20.7_@babel+core@7.20.12 '@babel/plugin-transform-async-to-generator': 7.20.7_@babel+core@7.20.12 '@babel/plugin-transform-block-scoped-functions': 7.18.6_@babel+core@7.20.12 - '@babel/plugin-transform-block-scoping': 7.20.14_@babel+core@7.20.12 + '@babel/plugin-transform-block-scoping': 7.20.15_@babel+core@7.20.12 '@babel/plugin-transform-classes': 7.20.7_@babel+core@7.20.12 '@babel/plugin-transform-computed-properties': 7.20.7_@babel+core@7.20.12 '@babel/plugin-transform-destructuring': 7.20.7_@babel+core@7.20.12 @@ -1272,6 +1280,10 @@ packages: esutils: 2.0.3 dev: true + /@babel/regjsgen/0.8.0: + resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==} + dev: true + /@babel/runtime/7.20.13: resolution: {integrity: sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==} engines: {node: '>=6.9.0'} @@ -1283,7 +1295,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.18.6 - '@babel/parser': 7.20.13 + '@babel/parser': 7.20.15 '@babel/types': 7.20.7 dev: true @@ -1297,7 +1309,7 @@ packages: '@babel/helper-function-name': 7.19.0 '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.20.13 + '@babel/parser': 7.20.15 '@babel/types': 7.20.7 debug: 4.3.4 globals: 11.12.0 @@ -1314,8 +1326,8 @@ packages: to-fast-properties: 2.0.0 dev: true - /@buf/meshtastic_protobufs.bufbuild_es/1.0.0-20230129055417-f11f156e79a6.1_@bufbuild+protobuf@1.0.0: - resolution: {registry: https://buf.build/gen/npm/v1, tarball: https://buf.build/gen/npm/v1/@buf/meshtastic_protobufs.bufbuild_es/1.0.0-20230129055417-f11f156e79a6.1/tarball} + /@buf/meshtastic_protobufs.bufbuild_es/1.0.0-20230211031647-2cce48659fb1.1_@bufbuild+protobuf@1.0.0: + resolution: {registry: https://buf.build/gen/npm/v1, tarball: https://buf.build/gen/npm/v1/@buf/meshtastic_protobufs.bufbuild_es/1.0.0-20230211031647-2cce48659fb1.1/tarball} peerDependencies: '@bufbuild/protobuf': ^1.0.0 dependencies: @@ -1557,6 +1569,30 @@ packages: - supports-color dev: true + /@floating-ui/core/0.7.3: + resolution: {integrity: sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==} + dev: false + + /@floating-ui/dom/0.5.4: + resolution: {integrity: sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==} + dependencies: + '@floating-ui/core': 0.7.3 + dev: false + + /@floating-ui/react-dom/0.7.2_zula6vjvt3wdocc4mwcxqa6nzi: + resolution: {integrity: sha512-1T0sJcpHgX/u4I1OzIEhlcrvkUN8ln39nz7fMoE/2HDHrPiMFoOGR7++GYyfUmIQHkkrTinaeQsO3XWubjSvGg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@floating-ui/dom': 0.5.4 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + use-isomorphic-layout-effect: 1.1.2_pmekkgnqduwlme35zpnqhenc34 + transitivePeerDependencies: + - '@types/react' + dev: false + /@gfx/zopfli/1.0.15: resolution: {integrity: sha512-7mBgpi7UD82fsff5ThQKet0uBTl4BYerQuc+/qA1ELTwWEiIedRTcD3JgiUu9wwZ2kytW8JOb165rSdAt8PfcQ==} engines: {node: '>= 8'} @@ -1564,27 +1600,7 @@ packages: base64-js: 1.5.1 dev: true - /@headlessui/react/1.7.8_biqbaboplfbrettd7655fr4n2y: - resolution: {integrity: sha512-zcwb0kd7L05hxmoAMIioEaOn235Dg0fUO+iGbLPgLVSjzl/l39V6DTpC2Df49PE5aG5/f5q0PZ9ZHZ78ENNV+A==} - engines: {node: '>=10'} - peerDependencies: - react: ^16 || ^17 || ^18 - react-dom: ^16 || ^17 || ^18 - dependencies: - client-only: 0.0.1 - react: 18.2.0 - react-dom: 18.2.0_react@18.2.0 - dev: false - - /@heroicons/react/2.0.14_react@18.2.0: - resolution: {integrity: sha512-eFL4if6L7woL1fUvk/jjXx4z9NxVESHHrQnEd2qKVelTFTIr/3VLGWdPb0biia9ZVe26P0JSs/xmOlRNur3SrQ==} - peerDependencies: - react: '>= 16' - dependencies: - react: 18.2.0 - dev: false - - /@hookform/error-message/2.0.1_uno7536xd2i6xn2dtvbywz2sva: + /@hookform/error-message/2.0.1_zf7ga3u4zrffjlingb6kh5ipva: resolution: {integrity: sha512-U410sAr92xgxT1idlu9WWOVjndxLdgPUHEB8Schr27C9eh7/xUnITWpCMF93s+lGiG++D4JnbSnrb5A21AdSNg==} peerDependencies: react: '>=16.8.0' @@ -1593,15 +1609,15 @@ packages: dependencies: react: 18.2.0 react-dom: 18.2.0_react@18.2.0 - react-hook-form: 7.42.1_react@18.2.0 + react-hook-form: 7.43.1_react@18.2.0 dev: false - /@hookform/resolvers/2.9.10_react-hook-form@7.42.1: - resolution: {integrity: sha512-JIL1DgJIlH9yuxcNGtyhsWX/PgNltz+5Gr6+8SX9fhXc/hPbEIk6wPI82nhgvp3uUb6ZfAM5mqg/x7KR7NAb+A==} + /@hookform/resolvers/2.9.11_react-hook-form@7.43.1: + resolution: {integrity: sha512-bA3aZ79UgcHj7tFV7RlgThzwSSHZgvfbt2wprldRkYBcMopdMvHyO17Wwp/twcJasNFischFfS7oz8Katz8DdQ==} peerDependencies: react-hook-form: ^7.0.0 dependencies: - react-hook-form: 7.42.1_react@18.2.0 + react-hook-form: 7.43.1_react@18.2.0 dev: false /@humanwhocodes/config-array/0.11.8: @@ -1669,16 +1685,12 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 dev: true - /@kurkle/color/0.3.2: - resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==} - dev: false - /@mapbox/geojson-rewind/0.5.2: resolution: {integrity: sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==} hasBin: true dependencies: get-stream: 6.0.1 - minimist: 1.2.7 + minimist: 1.2.8 dev: false /@mapbox/jsonlint-lines-primitives/2.0.2: @@ -1694,8 +1706,8 @@ packages: resolution: {integrity: sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==} dev: false - /@mapbox/tiny-sdf/2.0.5: - resolution: {integrity: sha512-OhXt2lS//WpLdkqrzo/KwB7SRD8AiNTFFzuo9n14IBupzIMa67yGItcK7I2W9D8Ghpa4T04Sw9FWsKCJG50Bxw==} + /@mapbox/tiny-sdf/2.0.6: + resolution: {integrity: sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA==} dev: false /@mapbox/unitbezier/0.0.1: @@ -1713,14 +1725,14 @@ packages: engines: {node: '>=6.0.0'} dev: false - /@meshtastic/meshtasticjs/2.0.15-0: - resolution: {integrity: sha512-CEfLP/PQWsQoUoFkQ2FVSNUKhjH+6J3QaTgvaRcP8YWq269SA8/A4ZFIIdg28DMKkPNcGPfVv/6PX7y98C6zmA==} + /@meshtastic/meshtasticjs/2.0.20-1: + resolution: {integrity: sha512-ShYcQ0/T4reWHcfgMIGhqjBQIIz4GrTZOJ9p+O9ChXxND+sWdmFe4U3e5sCMnEZW33xbI6bgQhq7De2sjqAzcQ==} dependencies: - '@buf/meshtastic_protobufs.bufbuild_es': 1.0.0-20230129055417-f11f156e79a6.1_@bufbuild+protobuf@1.0.0 + '@buf/meshtastic_protobufs.bufbuild_es': 1.0.0-20230211031647-2cce48659fb1.1_@bufbuild+protobuf@1.0.0 '@bufbuild/protobuf': 1.0.0 - crc: 4.3.1 + crc: 4.3.2 sub-events: 1.9.0 - tslog: 4.7.1 + tslog: 4.7.2 transitivePeerDependencies: - buffer dev: false @@ -1749,19 +1761,653 @@ packages: dependencies: cross-spawn: 7.0.3 is-glob: 4.0.3 - open: 8.4.0 + open: 8.4.1 picocolors: 1.0.0 tiny-glob: 0.2.9 tslib: 2.5.0 dev: true - /@primer/octicons-react/17.11.1_react@18.2.0: - resolution: {integrity: sha512-JGBgAHXKwzuPoOtys3ddmfD2h8GVhU9YwQgxLQD04+IGIgG8FfeXuGREfpIGQNszU4cPZtqw9dST9IONeUb0oQ==} - engines: {node: '>=8'} - peerDependencies: - react: '>=15' + /@radix-ui/number/1.0.0: + resolution: {integrity: sha512-Ofwh/1HX69ZfJRiRBMTy7rgjAzHmwe4kW9C9Y99HTRUcYLUuVT0KESFj15rPjRgKJs20GPq8Bm5aEDJ8DuA3vA==} dependencies: + '@babel/runtime': 7.20.13 + dev: false + + /@radix-ui/primitive/1.0.0: + resolution: {integrity: sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==} + dependencies: + '@babel/runtime': 7.20.13 + dev: false + + /@radix-ui/react-arrow/1.0.1_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-1yientwXqXcErDHEv8av9ZVNEBldH8L9scVR3is20lL+jOCfcJyMFZFEY5cgIrgexsq1qggSXqiEL/d/4f+QXA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@radix-ui/react-collection/1.0.1_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-uuiFbs+YCKjn3X1DTSx9G7BHApu4GHbi3kgiwsnFUbOKCrwejAJv4eE4Vc8C0Oaxt9T0aV4ox0WCOdx+39Xo+g==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-context': 1.0.0_react@18.2.0 + '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-slot': 1.0.1_react@18.2.0 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@radix-ui/react-compose-refs/1.0.0_react@18.2.0: + resolution: {integrity: sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + react: 18.2.0 + dev: false + + /@radix-ui/react-context/1.0.0_react@18.2.0: + resolution: {integrity: sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + react: 18.2.0 + dev: false + + /@radix-ui/react-dialog/1.0.0_zula6vjvt3wdocc4mwcxqa6nzi: + resolution: {integrity: sha512-Yn9YU+QlHYLWwV1XfKiqnGVpWYWk6MeBVM6x/bcoyPvxgjQGoeT35482viLPctTMWoMw0PoHgqfSox7Ig+957Q==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-context': 1.0.0_react@18.2.0 + '@radix-ui/react-dismissable-layer': 1.0.0_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-focus-guards': 1.0.0_react@18.2.0 + '@radix-ui/react-focus-scope': 1.0.0_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-id': 1.0.0_react@18.2.0 + '@radix-ui/react-portal': 1.0.0_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-primitive': 1.0.0_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-slot': 1.0.0_react@18.2.0 + '@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0 + aria-hidden: 1.2.2_pmekkgnqduwlme35zpnqhenc34 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + react-remove-scroll: 2.5.4_pmekkgnqduwlme35zpnqhenc34 + transitivePeerDependencies: + - '@types/react' + dev: false + + /@radix-ui/react-dialog/1.0.2_zula6vjvt3wdocc4mwcxqa6nzi: + resolution: {integrity: sha512-EKxxp2WNSmUPkx4trtWNmZ4/vAYEg7JkAfa1HKBUnaubw9eHzf1Orr9B472lJYaYz327RHDrd4R95fsw7VR8DA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-context': 1.0.0_react@18.2.0 + '@radix-ui/react-dismissable-layer': 1.0.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-focus-guards': 1.0.0_react@18.2.0 + '@radix-ui/react-focus-scope': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-id': 1.0.0_react@18.2.0 + '@radix-ui/react-portal': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-slot': 1.0.1_react@18.2.0 + '@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0 + aria-hidden: 1.2.2_pmekkgnqduwlme35zpnqhenc34 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + react-remove-scroll: 2.5.5_pmekkgnqduwlme35zpnqhenc34 + transitivePeerDependencies: + - '@types/react' + dev: false + + /@radix-ui/react-direction/1.0.0_react@18.2.0: + resolution: {integrity: sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + react: 18.2.0 + dev: false + + /@radix-ui/react-dismissable-layer/1.0.0_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-n7kDRfx+LB1zLueRDvZ1Pd0bxdJWDUZNQ/GWoxDn2prnuJKRdxsjulejX/ePkOsLi2tTm6P24mDqlMSgQpsT6g==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-primitive': 1.0.0_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0 + '@radix-ui/react-use-escape-keydown': 1.0.0_react@18.2.0 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@radix-ui/react-dismissable-layer/1.0.2_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-WjJzMrTWROozDqLB0uRWYvj4UuXsM/2L19EmQ3Au+IJWqwvwq9Bwd+P8ivo0Deg9JDPArR1I6MbWNi1CmXsskg==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0 + '@radix-ui/react-use-escape-keydown': 1.0.2_react@18.2.0 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@radix-ui/react-focus-guards/1.0.0_react@18.2.0: + resolution: {integrity: sha512-UagjDk4ijOAnGu4WMUPj9ahi7/zJJqNZ9ZAiGPp7waUWJO0O1aWXi/udPphI0IUjvrhBsZJGSN66dR2dsueLWQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + react: 18.2.0 + dev: false + + /@radix-ui/react-focus-scope/1.0.0_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-C4SWtsULLGf/2L4oGeIHlvWQx7Rf+7cX/vKOAD2dXW0A1b5QXwi3wWeaEgW+wn+SEVrraMUk05vLU9fZZz5HbQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-primitive': 1.0.0_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@radix-ui/react-focus-scope/1.0.1_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-Ej2MQTit8IWJiS2uuujGUmxXjF/y5xZptIIQnyd2JHLwtV0R2j9NRVoRj/1j/gJ7e3REdaBw4Hjf4a1ImhkZcQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@radix-ui/react-id/1.0.0_react@18.2.0: + resolution: {integrity: sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/react-use-layout-effect': 1.0.0_react@18.2.0 + react: 18.2.0 + dev: false + + /@radix-ui/react-label/2.0.0_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-7qCcZ3j2VQspWjy+gKR4W+V/z0XueQjeiZnlPOtsyiP9HaS8bfSU7ECoI3bvvdYntQj7NElW7OAYsYRW4MQvCg==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@radix-ui/react-menu/2.0.2_zula6vjvt3wdocc4mwcxqa6nzi: + resolution: {integrity: sha512-H5dtBi/k3tc45IMd2Pu+Q2PyONFlsYJ5sWUlflSs8BQRghh5GhJHLRuB1yb88VOywuzzvGkaR/HUJJ65Jf2POA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-collection': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-context': 1.0.0_react@18.2.0 + '@radix-ui/react-direction': 1.0.0_react@18.2.0 + '@radix-ui/react-dismissable-layer': 1.0.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-focus-guards': 1.0.0_react@18.2.0 + '@radix-ui/react-focus-scope': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-id': 1.0.0_react@18.2.0 + '@radix-ui/react-popper': 1.1.0_zula6vjvt3wdocc4mwcxqa6nzi + '@radix-ui/react-portal': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-roving-focus': 1.0.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-slot': 1.0.1_react@18.2.0 + '@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0 + aria-hidden: 1.2.2_pmekkgnqduwlme35zpnqhenc34 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + react-remove-scroll: 2.5.5_pmekkgnqduwlme35zpnqhenc34 + transitivePeerDependencies: + - '@types/react' + dev: false + + /@radix-ui/react-menubar/1.0.0_zula6vjvt3wdocc4mwcxqa6nzi: + resolution: {integrity: sha512-l3pWX1Xg4xUVE8M+bxNl2FxfmntfMjW5vgVNkmXqyyiWipmayOEJUs75jFKwtZvYI9o/S5S+20u+N8+h4O2OyQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-collection': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-context': 1.0.0_react@18.2.0 + '@radix-ui/react-direction': 1.0.0_react@18.2.0 + '@radix-ui/react-id': 1.0.0_react@18.2.0 + '@radix-ui/react-menu': 2.0.2_zula6vjvt3wdocc4mwcxqa6nzi + '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-roving-focus': 1.0.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + transitivePeerDependencies: + - '@types/react' + dev: false + + /@radix-ui/react-popover/1.0.3_zula6vjvt3wdocc4mwcxqa6nzi: + resolution: {integrity: sha512-YwedSukfWsyJs3/yP3yXUq44k4/JBe3jqU63Z8v2i19qZZ3dsx32oma17ztgclWPNuqp3A+Xa9UiDlZHyVX8Vg==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-context': 1.0.0_react@18.2.0 + '@radix-ui/react-dismissable-layer': 1.0.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-focus-guards': 1.0.0_react@18.2.0 + '@radix-ui/react-focus-scope': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-id': 1.0.0_react@18.2.0 + '@radix-ui/react-popper': 1.1.0_zula6vjvt3wdocc4mwcxqa6nzi + '@radix-ui/react-portal': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-slot': 1.0.1_react@18.2.0 + '@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0 + aria-hidden: 1.2.2_pmekkgnqduwlme35zpnqhenc34 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + react-remove-scroll: 2.5.5_pmekkgnqduwlme35zpnqhenc34 + transitivePeerDependencies: + - '@types/react' + dev: false + + /@radix-ui/react-popper/1.1.0_zula6vjvt3wdocc4mwcxqa6nzi: + resolution: {integrity: sha512-07U7jpI0dZcLRAxT7L9qs6HecSoPhDSJybF7mEGHJDBDv+ZoGCvIlva0s+WxMXwJEav+ckX3hAlXBtnHmuvlCQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@floating-ui/react-dom': 0.7.2_zula6vjvt3wdocc4mwcxqa6nzi + '@radix-ui/react-arrow': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-context': 1.0.0_react@18.2.0 + '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0 + '@radix-ui/react-use-layout-effect': 1.0.0_react@18.2.0 + '@radix-ui/react-use-rect': 1.0.0_react@18.2.0 + '@radix-ui/react-use-size': 1.0.0_react@18.2.0 + '@radix-ui/rect': 1.0.0 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + transitivePeerDependencies: + - '@types/react' + dev: false + + /@radix-ui/react-portal/1.0.0_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-a8qyFO/Xb99d8wQdu4o7qnigNjTPG123uADNecz0eX4usnQEj7o+cG4ZX4zkqq98NYekT7UoEQIjxBNWIFuqTA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/react-primitive': 1.0.0_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@radix-ui/react-portal/1.0.1_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-NY2vUWI5WENgAT1nfC6JS7RU5xRYBfjZVLq0HmgEN1Ezy3rk/UruMV4+Rd0F40PEaFC5SrLS1ixYvcYIQrb4Ig==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@radix-ui/react-presence/1.0.0_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-use-layout-effect': 1.0.0_react@18.2.0 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@radix-ui/react-primitive/1.0.0_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-EyXe6mnRlHZ8b6f4ilTDrXmkLShICIuOTTj0GX4w1rp+wSxf3+TD05u1UOITC8VsJ2a9nwHvdXtOXEOl0Cw/zQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/react-slot': 1.0.0_react@18.2.0 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@radix-ui/react-primitive/1.0.1_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/react-slot': 1.0.1_react@18.2.0 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@radix-ui/react-roving-focus/1.0.2_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-HLK+CqD/8pN6GfJm3U+cqpqhSKYAWiOJDe+A+8MfxBnOue39QEeMa43csUn2CXCHQT0/mewh1LrrG4tfkM9DMA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-collection': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-context': 1.0.0_react@18.2.0 + '@radix-ui/react-direction': 1.0.0_react@18.2.0 + '@radix-ui/react-id': 1.0.0_react@18.2.0 + '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0 + '@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@radix-ui/react-select/1.2.0_zula6vjvt3wdocc4mwcxqa6nzi: + resolution: {integrity: sha512-MmXKsIBrG9GKxt8JKIn75LEPiX/zejBmj/Z36Hxtm9cdmCFzTo78QJ0Q3buLGzr0c3lzXdfgeKntmgCzaGxgkw==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/number': 1.0.0 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-collection': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-context': 1.0.0_react@18.2.0 + '@radix-ui/react-direction': 1.0.0_react@18.2.0 + '@radix-ui/react-dismissable-layer': 1.0.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-focus-guards': 1.0.0_react@18.2.0 + '@radix-ui/react-focus-scope': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-id': 1.0.0_react@18.2.0 + '@radix-ui/react-popper': 1.1.0_zula6vjvt3wdocc4mwcxqa6nzi + '@radix-ui/react-portal': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-slot': 1.0.1_react@18.2.0 + '@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0 + '@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0 + '@radix-ui/react-use-layout-effect': 1.0.0_react@18.2.0 + '@radix-ui/react-use-previous': 1.0.0_react@18.2.0 + '@radix-ui/react-visually-hidden': 1.0.1_biqbaboplfbrettd7655fr4n2y + aria-hidden: 1.2.2_pmekkgnqduwlme35zpnqhenc34 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + react-remove-scroll: 2.5.5_pmekkgnqduwlme35zpnqhenc34 + transitivePeerDependencies: + - '@types/react' + dev: false + + /@radix-ui/react-separator/1.0.1_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-uc6Izot0D8uVz6T2nSb/HI7OaxkeaD50GgKr3W6HORnbfGVrG7LWuy+g6Fd58n8wHbrRblSYJZEfcjgymMlJjw==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@radix-ui/react-slot/1.0.0_react@18.2.0: + resolution: {integrity: sha512-3mrKauI/tWXo1Ll+gN5dHcxDPdm/Df1ufcDLCecn+pnCIVcdWE7CujXo8QaXOWRJyZyQWWbpB8eFwHzWXlv5mQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + react: 18.2.0 + dev: false + + /@radix-ui/react-slot/1.0.1_react@18.2.0: + resolution: {integrity: sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + react: 18.2.0 + dev: false + + /@radix-ui/react-switch/1.0.1_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-tTxGluMtwrc5ffgAiOSMrYIx0r3vSTcgM4Vl8rqfpXcHt6ryB9B0OlFKUOiDpKASXlhvzfHf4Y0AYKJdpzjL8w==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-context': 1.0.0_react@18.2.0 + '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0 + '@radix-ui/react-use-previous': 1.0.0_react@18.2.0 + '@radix-ui/react-use-size': 1.0.0_react@18.2.0 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@radix-ui/react-tabs/1.0.2_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-gOUwh+HbjCuL0UCo8kZ+kdUEG8QtpdO4sMQduJ34ZEz0r4922g9REOBM+vIsfwtGxSug4Yb1msJMJYN2Bk8TpQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-context': 1.0.0_react@18.2.0 + '@radix-ui/react-direction': 1.0.0_react@18.2.0 + '@radix-ui/react-id': 1.0.0_react@18.2.0 + '@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-roving-focus': 1.0.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@radix-ui/react-toast/1.1.2_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-Kpr4BBYoP0O5A1UeDBmao87UnCMNdAKGNioQH5JzEm6OYTUVGhuDRbOwoZxPwOZ6vsjJHeIpdUrwbiHEB65CCw==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-collection': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-context': 1.0.0_react@18.2.0 + '@radix-ui/react-dismissable-layer': 1.0.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-portal': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0 + '@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0 + '@radix-ui/react-use-layout-effect': 1.0.0_react@18.2.0 + '@radix-ui/react-visually-hidden': 1.0.1_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@radix-ui/react-tooltip/1.0.3_zula6vjvt3wdocc4mwcxqa6nzi: + resolution: {integrity: sha512-cmc9qV4KpgqdXVTn1K8KN8MnuSXvw+E719pKwyvpCGrQ+0AA2qTjcIL3uxCj4jc4k3sDR36RF7R3H7N5hPybBQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/primitive': 1.0.0 + '@radix-ui/react-compose-refs': 1.0.0_react@18.2.0 + '@radix-ui/react-context': 1.0.0_react@18.2.0 + '@radix-ui/react-dismissable-layer': 1.0.2_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-id': 1.0.0_react@18.2.0 + '@radix-ui/react-popper': 1.1.0_zula6vjvt3wdocc4mwcxqa6nzi + '@radix-ui/react-portal': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y + '@radix-ui/react-slot': 1.0.1_react@18.2.0 + '@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0 + '@radix-ui/react-visually-hidden': 1.0.1_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + transitivePeerDependencies: + - '@types/react' + dev: false + + /@radix-ui/react-use-callback-ref/1.0.0_react@18.2.0: + resolution: {integrity: sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-controllable-state/1.0.0_react@18.2.0: + resolution: {integrity: sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-escape-keydown/1.0.0_react@18.2.0: + resolution: {integrity: sha512-JwfBCUIfhXRxKExgIqGa4CQsiMemo1Xt0W/B4ei3fpzpvPENKpMKQ8mZSB6Acj3ebrAEgi2xiQvcI1PAAodvyg==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-escape-keydown/1.0.2_react@18.2.0: + resolution: {integrity: sha512-DXGim3x74WgUv+iMNCF+cAo8xUHHeqvjx8zs7trKf+FkQKPQXLk2sX7Gx1ysH7Q76xCpZuxIJE7HLPxRE+Q+GA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-layout-effect/1.0.0_react@18.2.0: + resolution: {integrity: sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-previous/1.0.0_react@18.2.0: + resolution: {integrity: sha512-RG2K8z/K7InnOKpq6YLDmT49HGjNmrK+fr82UCVKT2sW0GYfVnYp4wZWBooT/EYfQ5faA9uIjvsuMMhH61rheg==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-rect/1.0.0_react@18.2.0: + resolution: {integrity: sha512-TB7pID8NRMEHxb/qQJpvSt3hQU4sqNPM1VCTjTRjEOa7cEop/QMuq8S6fb/5Tsz64kqSvB9WnwsDHtjnrM9qew==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/rect': 1.0.0 + react: 18.2.0 + dev: false + + /@radix-ui/react-use-size/1.0.0_react@18.2.0: + resolution: {integrity: sha512-imZ3aYcoYCKhhgNpkNDh/aTiU05qw9hX+HHI1QDBTyIlcFjgeFlKKySNGMwTp7nYFLQg/j0VA2FmCY4WPDDHMg==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/react-use-layout-effect': 1.0.0_react@18.2.0 + react: 18.2.0 + dev: false + + /@radix-ui/react-visually-hidden/1.0.1_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-K1hJcCMfWfiYUibRqf3V8r5Drpyf7rh44jnrwAbdvI5iCCijilBBeyQv9SKidYNZIopMdCyR9FnIjkHxHN0FcQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + dependencies: + '@babel/runtime': 7.20.13 + '@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@radix-ui/rect/1.0.0: + resolution: {integrity: sha512-d0O68AYy/9oeEy1DdC07bz1/ZXX+DqCskRd3i4JzLSTXwefzaepQrKjXC7aNM8lTHjFLDO0pDgaEiQ7jEk+HVg==} + dependencies: + '@babel/runtime': 7.20.13 dev: false /@rollup/plugin-babel/5.3.1_3dsfpkpoyvuuxyfgdbpn4j4uzm: @@ -1790,7 +2436,7 @@ packages: '@rollup/pluginutils': 3.1.0_rollup@2.79.1 '@types/resolve': 1.17.1 builtin-modules: 3.3.0 - deepmerge: 4.2.2 + deepmerge: 4.3.0 is-module: 1.0.0 resolve: 1.22.1 rollup: 2.79.1 @@ -1806,7 +2452,7 @@ packages: rollup: 2.79.1 dev: true - /@rollup/plugin-replace/5.0.2_rollup@3.12.0: + /@rollup/plugin-replace/5.0.2_rollup@3.15.0: resolution: {integrity: sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1815,9 +2461,9 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.0.2_rollup@3.12.0 + '@rollup/pluginutils': 5.0.2_rollup@3.15.0 magic-string: 0.27.0 - rollup: 3.12.0 + rollup: 3.15.0 dev: true /@rollup/pluginutils/3.1.0_rollup@2.79.1: @@ -1832,7 +2478,7 @@ packages: rollup: 2.79.1 dev: true - /@rollup/pluginutils/5.0.2_rollup@3.12.0: + /@rollup/pluginutils/5.0.2_rollup@3.15.0: resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1844,7 +2490,7 @@ packages: '@types/estree': 1.0.0 estree-walker: 2.0.2 picomatch: 2.3.1 - rollup: 3.12.0 + rollup: 3.15.0 dev: true /@stablelib/binary/1.0.1: @@ -1882,16 +2528,16 @@ packages: string.prototype.matchall: 4.0.8 dev: true - /@tailwindcss/forms/0.5.3_tailwindcss@3.2.4: + /@tailwindcss/forms/0.5.3_tailwindcss@3.2.6: resolution: {integrity: sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==} peerDependencies: tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1' dependencies: mini-svg-data-uri: 1.4.4 - tailwindcss: 3.2.4_postcss@8.4.21 + tailwindcss: 3.2.6_postcss@8.4.21 dev: true - /@tailwindcss/typography/0.5.9_tailwindcss@3.2.4: + /@tailwindcss/typography/0.5.9_tailwindcss@3.2.6: resolution: {integrity: sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==} peerDependencies: tailwindcss: '>=3.0.0 || insiders' @@ -1900,7 +2546,7 @@ packages: lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 postcss-selector-parser: 6.0.10 - tailwindcss: 3.2.4_postcss@8.4.21 + tailwindcss: 3.2.6_postcss@8.4.21 dev: false /@turf/along/6.5.0: @@ -2933,12 +3579,8 @@ packages: d3-voronoi: 1.1.2 dev: false - /@types/base16/1.0.2: - resolution: {integrity: sha512-oYO/U4VD1DavwrKuCSQWdLG+5K22SLPem2OQaHmFcQuwHoVeGC+JGVRji2MUqZUAIQZHEonOeVfAX09hYiLsdg==} - dev: false - - /@types/chrome/0.0.210: - resolution: {integrity: sha512-VSjQu1k6a/rAfuqR1Gi/oxHZj4+t6+LG+GobNI3ZWI6DQ+fmphNSF6TrLHG6BYK2bXc9Gb4c1uXFKRRVLaGl5Q==} + /@types/chrome/0.0.212: + resolution: {integrity: sha512-O9blKfj6mQyBvkexEa71xcpRfkjAu8izQD3qGYfdwffk+mJhF7eogz628bZr5dETT6Eu7vU0stUGYG/+EQWj9g==} dependencies: '@types/filesystem': 0.0.32 '@types/har-format': 1.2.10 @@ -2986,10 +3628,6 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true - /@types/lodash/4.14.191: - resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==} - dev: false - /@types/mapbox-gl/2.7.10: resolution: {integrity: sha512-nMVEcu9bAcenvx6oPWubQSPevsekByjOfKjlkr+8P91vawtkxTnopDoXXq1Qn/f4cg3zt0Z2W9DVsVsKRNXJTw==} dependencies: @@ -3008,8 +3646,8 @@ packages: '@types/pbf': 3.0.2 dev: false - /@types/node/18.11.18: - resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} + /@types/node/18.13.0: + resolution: {integrity: sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==} dev: true /@types/pbf/3.0.2: @@ -3022,11 +3660,11 @@ packages: /@types/react-dom/18.0.10: resolution: {integrity: sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg==} dependencies: - '@types/react': 18.0.27 + '@types/react': 18.0.28 dev: true - /@types/react/18.0.27: - resolution: {integrity: sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA==} + /@types/react/18.0.28: + resolution: {integrity: sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==} dependencies: '@types/prop-types': 15.7.5 '@types/scheduler': 0.16.2 @@ -3035,7 +3673,7 @@ packages: /@types/resolve/1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} dependencies: - '@types/node': 18.11.18 + '@types/node': 18.13.0 dev: true /@types/scheduler/0.16.2: @@ -3049,8 +3687,8 @@ packages: resolution: {integrity: sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==} dev: true - /@types/validator/13.7.11: - resolution: {integrity: sha512-WqTos+CnAKN64YwyBMhgUYhb5VsTNKwUY6AuzG5qu9/pFZJar/RJFMZBXwX7VS+uzYi+lIAr3WkvuWqEI9F2eg==} + /@types/validator/13.7.12: + resolution: {integrity: sha512-YVtyAPqpefU+Mm/qqnOANW6IkqKpCSrarcyV269C8MA8Ux0dbkEuQwM/4CjL47kVEM2LgBef/ETfkH+c6+moFA==} dev: false /@types/w3c-web-serial/1.0.3: @@ -3061,8 +3699,8 @@ packages: resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==} dev: true - /@typescript-eslint/eslint-plugin/5.49.0_rsaczafy73x3xqauzesvzbsgzy: - resolution: {integrity: sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==} + /@typescript-eslint/eslint-plugin/5.51.0_z4swst3wuuqk4hlme4ajzslgh4: + resolution: {integrity: sha512-wcAwhEWm1RgNd7dxD/o+nnLW8oH+6RK1OGnmbmkj/GGoDPV1WWMVP0FXYQBivKHdwM1pwii3bt//RC62EriIUQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 @@ -3072,24 +3710,25 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.49.0_zkdaqh7it7uc4cvz2haft7rc6u - '@typescript-eslint/scope-manager': 5.49.0 - '@typescript-eslint/type-utils': 5.49.0_zkdaqh7it7uc4cvz2haft7rc6u - '@typescript-eslint/utils': 5.49.0_zkdaqh7it7uc4cvz2haft7rc6u + '@typescript-eslint/parser': 5.51.0_7kw3g6rralp5ps6mg3uyzz6azm + '@typescript-eslint/scope-manager': 5.51.0 + '@typescript-eslint/type-utils': 5.51.0_7kw3g6rralp5ps6mg3uyzz6azm + '@typescript-eslint/utils': 5.51.0_7kw3g6rralp5ps6mg3uyzz6azm debug: 4.3.4 - eslint: 8.33.0 + eslint: 8.34.0 + grapheme-splitter: 1.0.4 ignore: 5.2.4 natural-compare-lite: 1.4.0 regexpp: 3.2.0 semver: 7.3.8 - tsutils: 3.21.0_typescript@4.9.4 - typescript: 4.9.4 + tsutils: 3.21.0_typescript@4.9.5 + typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser/5.49.0_zkdaqh7it7uc4cvz2haft7rc6u: - resolution: {integrity: sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg==} + /@typescript-eslint/parser/5.51.0_7kw3g6rralp5ps6mg3uyzz6azm: + resolution: {integrity: sha512-fEV0R9gGmfpDeRzJXn+fGQKcl0inIeYobmmUWijZh9zA7bxJ8clPhV9up2ZQzATxAiFAECqPQyMDB4o4B81AaA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -3098,26 +3737,26 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 5.49.0 - '@typescript-eslint/types': 5.49.0 - '@typescript-eslint/typescript-estree': 5.49.0_typescript@4.9.4 + '@typescript-eslint/scope-manager': 5.51.0 + '@typescript-eslint/types': 5.51.0 + '@typescript-eslint/typescript-estree': 5.51.0_typescript@4.9.5 debug: 4.3.4 - eslint: 8.33.0 - typescript: 4.9.4 + eslint: 8.34.0 + typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/scope-manager/5.49.0: - resolution: {integrity: sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==} + /@typescript-eslint/scope-manager/5.51.0: + resolution: {integrity: sha512-gNpxRdlx5qw3yaHA0SFuTjW4rxeYhpHxt491PEcKF8Z6zpq0kMhe0Tolxt0qjlojS+/wArSDlj/LtE69xUJphQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.49.0 - '@typescript-eslint/visitor-keys': 5.49.0 + '@typescript-eslint/types': 5.51.0 + '@typescript-eslint/visitor-keys': 5.51.0 dev: true - /@typescript-eslint/type-utils/5.49.0_zkdaqh7it7uc4cvz2haft7rc6u: - resolution: {integrity: sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==} + /@typescript-eslint/type-utils/5.51.0_7kw3g6rralp5ps6mg3uyzz6azm: + resolution: {integrity: sha512-QHC5KKyfV8sNSyHqfNa0UbTbJ6caB8uhcx2hYcWVvJAZYJRBo5HyyZfzMdRx8nvS+GyMg56fugMzzWnojREuQQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -3126,23 +3765,23 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.49.0_typescript@4.9.4 - '@typescript-eslint/utils': 5.49.0_zkdaqh7it7uc4cvz2haft7rc6u + '@typescript-eslint/typescript-estree': 5.51.0_typescript@4.9.5 + '@typescript-eslint/utils': 5.51.0_7kw3g6rralp5ps6mg3uyzz6azm debug: 4.3.4 - eslint: 8.33.0 - tsutils: 3.21.0_typescript@4.9.4 - typescript: 4.9.4 + eslint: 8.34.0 + tsutils: 3.21.0_typescript@4.9.5 + typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types/5.49.0: - resolution: {integrity: sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg==} + /@typescript-eslint/types/5.51.0: + resolution: {integrity: sha512-SqOn0ANn/v6hFn0kjvLwiDi4AzR++CBZz0NV5AnusT2/3y32jdc0G4woXPWHCumWtUXZKPAS27/9vziSsC9jnw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree/5.49.0_typescript@4.9.4: - resolution: {integrity: sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==} + /@typescript-eslint/typescript-estree/5.51.0_typescript@4.9.5: + resolution: {integrity: sha512-TSkNupHvNRkoH9FMA3w7TazVFcBPveAAmb7Sz+kArY6sLT86PA5Vx80cKlYmd8m3Ha2SwofM1KwraF24lM9FvA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -3150,58 +3789,58 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 5.49.0 - '@typescript-eslint/visitor-keys': 5.49.0 + '@typescript-eslint/types': 5.51.0 + '@typescript-eslint/visitor-keys': 5.51.0 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 semver: 7.3.8 - tsutils: 3.21.0_typescript@4.9.4 - typescript: 4.9.4 + tsutils: 3.21.0_typescript@4.9.5 + typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils/5.49.0_zkdaqh7it7uc4cvz2haft7rc6u: - resolution: {integrity: sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==} + /@typescript-eslint/utils/5.51.0_7kw3g6rralp5ps6mg3uyzz6azm: + resolution: {integrity: sha512-76qs+5KWcaatmwtwsDJvBk4H76RJQBFe+Gext0EfJdC3Vd2kpY2Pf//OHHzHp84Ciw0/rYoGTDnIAr3uWhhJYw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: '@types/json-schema': 7.0.11 '@types/semver': 7.3.13 - '@typescript-eslint/scope-manager': 5.49.0 - '@typescript-eslint/types': 5.49.0 - '@typescript-eslint/typescript-estree': 5.49.0_typescript@4.9.4 - eslint: 8.33.0 + '@typescript-eslint/scope-manager': 5.51.0 + '@typescript-eslint/types': 5.51.0 + '@typescript-eslint/typescript-estree': 5.51.0_typescript@4.9.5 + eslint: 8.34.0 eslint-scope: 5.1.1 - eslint-utils: 3.0.0_eslint@8.33.0 + eslint-utils: 3.0.0_eslint@8.34.0 semver: 7.3.8 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/visitor-keys/5.49.0: - resolution: {integrity: sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==} + /@typescript-eslint/visitor-keys/5.51.0: + resolution: {integrity: sha512-Oh2+eTdjHjOFjKA27sxESlA87YPSOJafGCR0md5oeMdh1ZcCfAGCIOL216uTBAkAIptvLIfKQhl7lHxMJet4GQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.49.0 + '@typescript-eslint/types': 5.51.0 eslint-visitor-keys: 3.3.0 dev: true - /@vitejs/plugin-react/3.0.1_vite@4.0.4: - resolution: {integrity: sha512-mx+QvYwIbbpOIJw+hypjnW1lAbKDHtWK5ibkF/V1/oMBu8HU/chb+SnqJDAsLq1+7rGqjktCEomMTM5KShzUKQ==} + /@vitejs/plugin-react/3.1.0_vite@4.1.1: + resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: - vite: ^4.0.0 + vite: ^4.1.0-beta.0 dependencies: '@babel/core': 7.20.12 '@babel/plugin-transform-react-jsx-self': 7.18.6_@babel+core@7.20.12 '@babel/plugin-transform-react-jsx-source': 7.19.6_@babel+core@7.20.12 magic-string: 0.27.0 react-refresh: 0.14.0 - vite: 4.0.4_@types+node@18.11.18 + vite: 4.1.1_@types+node@18.13.0 transitivePeerDependencies: - supports-color dev: true @@ -3287,12 +3926,27 @@ packages: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true + /aria-hidden/1.2.2_pmekkgnqduwlme35zpnqhenc34: + resolution: {integrity: sha512-6y/ogyDTk/7YAe91T3E2PR1ALVKyM2QbTio5HwM+N1Q6CMlCKhvClyIjkckBswa0f2xJhjsfzIGa1yVSe1UMVA==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0 + react: ^16.9.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.28 + react: 18.2.0 + tslib: 2.5.0 + dev: false + /array-includes/3.1.6: resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 es-abstract: 1.21.1 get-intrinsic: 1.2.0 is-string: 1.0.7 @@ -3308,7 +3962,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 es-abstract: 1.21.1 es-shim-unscopables: 1.0.0 dev: true @@ -3318,7 +3972,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 es-abstract: 1.21.1 es-shim-unscopables: 1.0.0 dev: true @@ -3327,7 +3981,7 @@ packages: resolution: {integrity: sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 es-abstract: 1.21.1 es-shim-unscopables: 1.0.0 get-intrinsic: 1.2.0 @@ -3349,8 +4003,8 @@ packages: peerDependencies: postcss: ^8.1.0 dependencies: - browserslist: 4.21.4 - caniuse-lite: 1.0.30001449 + browserslist: 4.21.5 + caniuse-lite: 1.0.30001451 fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 @@ -3403,10 +4057,6 @@ packages: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true - /base16/1.0.0: - resolution: {integrity: sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==} - dev: false - /base64-js/1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -3433,15 +4083,15 @@ packages: dependencies: fill-range: 7.0.1 - /browserslist/4.21.4: - resolution: {integrity: sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==} + /browserslist/4.21.5: + resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001449 - electron-to-chromium: 1.4.284 - node-releases: 2.0.8 - update-browserslist-db: 1.0.10_browserslist@4.21.4 + caniuse-lite: 1.0.30001451 + electron-to-chromium: 1.4.295 + node-releases: 2.0.10 + update-browserslist-db: 1.0.10_browserslist@4.21.5 dev: true /buffer-from/1.1.2: @@ -3468,8 +4118,8 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - /caniuse-lite/1.0.30001449: - resolution: {integrity: sha512-CPB+UL9XMT/Av+pJxCKGhdx+yg1hzplvFJQlJ2n68PyQGMz9L/E2zCyLdOL8uasbouTUgnPl+y0tccI/se+BEw==} + /caniuse-lite/1.0.30001451: + resolution: {integrity: sha512-XY7UbUpGRatZzoRft//5xOa69/1iGJRBlrieH6QYrkKLIFn3m7OVEJ81dSrKoy2BnKsdbX5cLrOispZNYo9v2w==} dev: true /chalk/2.4.2: @@ -3489,23 +4139,6 @@ packages: supports-color: 7.2.0 dev: true - /chart.js/4.2.0: - resolution: {integrity: sha512-wbtcV+QKeH0F7gQZaCJEIpsNriFheacouJQTVIjITi3eQA8bTlIBoknz0+dgV79aeKLNMAX+nDslIVE/nJ3rzA==} - engines: {pnpm: ^7.0.0} - dependencies: - '@kurkle/color': 0.3.2 - dev: false - - /chartjs-adapter-date-fns/3.0.0_n6szoxj4ax2zhp2sxsxxj6zdla: - resolution: {integrity: sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==} - peerDependencies: - chart.js: '>=2.8.0' - date-fns: '>=2.0.0' - dependencies: - chart.js: 4.2.0 - date-fns: 2.29.3 - dev: false - /chokidar/3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} @@ -3532,13 +4165,20 @@ packages: /class-validator/0.14.0: resolution: {integrity: sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A==} dependencies: - '@types/validator': 13.7.11 - libphonenumber-js: 1.10.18 - validator: 13.7.0 + '@types/validator': 13.7.12 + libphonenumber-js: 1.10.19 + validator: 13.9.0 dev: false - /client-only/0.0.1: - resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + /class-variance-authority/0.4.0_typescript@4.9.5: + resolution: {integrity: sha512-74enNN8O9ZNieycac/y8FxqgyzZhZbxmCitAtAeUrLPlxjSd5zA7LfpprmxEcOmQBnaGs5hYhiSGnJ0mqrtBLQ==} + peerDependencies: + typescript: '>= 4.5.5 < 5' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + typescript: 4.9.5 dev: false /cliui/8.0.1: @@ -3550,10 +4190,30 @@ packages: wrap-ansi: 7.0.0 dev: true + /clsx/1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + dev: false + + /cmdk/0.1.22_zula6vjvt3wdocc4mwcxqa6nzi: + resolution: {integrity: sha512-F0ffliBO/U6SXKGRud9AjsNuINvfF+d9BVUgi/y2+v3TSBHVOmIXl43CWLEyEEIOwc2DTnlXvIQenhGAEpwW0Q==} + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + dependencies: + '@radix-ui/react-dialog': 1.0.0_zula6vjvt3wdocc4mwcxqa6nzi + command-score: 0.1.2 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + transitivePeerDependencies: + - '@types/react' + dev: false + /color-convert/1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: color-name: 1.1.3 + dev: true /color-convert/2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} @@ -3564,22 +4224,13 @@ packages: /color-name/1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true /color-name/1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - /color-string/1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} - dependencies: - color-name: 1.1.4 - simple-swizzle: 0.2.2 - dev: false - - /color/3.2.1: - resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} - dependencies: - color-convert: 1.9.3 - color-string: 1.9.1 + /command-score/0.1.2: + resolution: {integrity: sha512-VtDvQpIJBvBatnONUsPzXYFVKQQAhuf3XTNOAsdBxCNO/QCtUUd8LSgjn0GVarBkCad6aJCZfXgrjYbl/KRr7w==} dev: false /commander/2.20.3: @@ -3615,18 +4266,21 @@ packages: /core-js-compat/3.27.2: resolution: {integrity: sha512-welaYuF7ZtbYKGrIy7y3eb40d37rG1FvzEOfe7hSLd2iD6duMDqUhRfSvCGyC46HhR6Y8JXXdZ2lnRUMkPBpvg==} dependencies: - browserslist: 4.21.4 + browserslist: 4.21.5 dev: true /core-util-is/1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} dev: true - /crc/4.3.1: - resolution: {integrity: sha512-xj9aHWoSZ3QjIrfhaDjWFObTOuCKgI4PDuzORvgzCIJ3qDw9Z3dOVK/Oj++N0kWdFLWBbOjSi+/0SLleiy/4OQ==} + /crc/4.3.2: + resolution: {integrity: sha512-uGDHf4KLLh2zsHa8D8hIQ1H/HtFQhyHrc0uhHBcoKGol/Xnb+MPYfUMw7cvON6ze/GUESTudKayDcJC5HnJv1A==} engines: {node: '>=12'} peerDependencies: buffer: '>=6.0.3' + peerDependenciesMeta: + buffer: + optional: true dev: false /cross-spawn/7.0.3: @@ -3669,11 +4323,6 @@ packages: resolution: {integrity: sha1-Fodmfo8TotFYyAwUgMWinLDYlzw=} dev: false - /date-fns/2.29.3: - resolution: {integrity: sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==} - engines: {node: '>=0.11'} - dev: false - /debug/3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -3734,8 +4383,8 @@ packages: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true - /deepmerge/4.2.2: - resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} + /deepmerge/4.3.0: + resolution: {integrity: sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==} engines: {node: '>=0.10.0'} dev: true @@ -3744,8 +4393,8 @@ packages: engines: {node: '>=8'} dev: true - /define-properties/1.1.4: - resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} + /define-properties/1.2.0: + resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} engines: {node: '>= 0.4'} dependencies: has-property-descriptors: 1.0.0 @@ -3758,6 +4407,10 @@ packages: resolution: {integrity: sha1-3J9ZyPCrl+FiSsZJMP0xlIF9ysU=} dev: false + /detect-node-es/1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + dev: false + /detective/5.2.1: resolution: {integrity: sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==} engines: {node: '>=0.8.0'} @@ -3765,7 +4418,7 @@ packages: dependencies: acorn-node: 1.8.2 defined: 1.0.1 - minimist: 1.2.7 + minimist: 1.2.8 /didyoumean/1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} @@ -3819,8 +4472,8 @@ packages: jake: 10.8.5 dev: true - /electron-to-chromium/1.4.284: - resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==} + /electron-to-chromium/1.4.295: + resolution: {integrity: sha512-lEO94zqf1bDA3aepxwnWoHUjA8sZ+2owgcSZjYQy0+uOSEclJX0VieZC+r+wLpSxUHRd6gG32znTWmr+5iGzFw==} dev: true /emoji-regex/8.0.0: @@ -3863,7 +4516,7 @@ packages: has-property-descriptors: 1.0.0 has-proto: 1.0.1 has-symbols: 1.0.3 - internal-slot: 1.0.4 + internal-slot: 1.0.5 is-array-buffer: 3.0.1 is-callable: 1.2.7 is-negative-zero: 2.0.2 @@ -3967,13 +4620,13 @@ packages: engines: {node: '>=10'} dev: true - /eslint-config-prettier/8.6.0_eslint@8.33.0: + /eslint-config-prettier/8.6.0_eslint@8.34.0: resolution: {integrity: sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 8.33.0 + eslint: 8.34.0 dev: true /eslint-import-resolver-node/0.3.7: @@ -3986,7 +4639,7 @@ packages: - supports-color dev: true - /eslint-import-resolver-typescript/3.5.3_ohdts44xlqyeyrlje4qnefqeay: + /eslint-import-resolver-typescript/3.5.3_mvgyw3chnqkp6sgfmmtihyjpnm: resolution: {integrity: sha512-njRcKYBc3isE42LaTcJNVANR3R99H9bAxBDMNDr2W7yq5gYPxbU3MkdhsQukxZ/Xg9C2vcyLlDsbKfRDg0QvCQ==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -3995,9 +4648,9 @@ packages: dependencies: debug: 4.3.4 enhanced-resolve: 5.12.0 - eslint: 8.33.0 - eslint-plugin-import: 2.27.5_c6pjvta7aysubsdnhvgec47vxe - get-tsconfig: 4.3.0 + eslint: 8.34.0 + eslint-plugin-import: 2.27.5_inmo4nrctlhmfx73hzu6aogupa + get-tsconfig: 4.4.0 globby: 13.1.3 is-core-module: 2.11.0 is-glob: 4.0.3 @@ -4006,7 +4659,7 @@ packages: - supports-color dev: true - /eslint-module-utils/2.7.4_5hqjyc62h4cn6t4zslmr6l4rke: + /eslint-module-utils/2.7.4_7d5otxxipfjcaqho6psg7iyp4e: resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} engines: {node: '>=4'} peerDependencies: @@ -4027,16 +4680,16 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.49.0_zkdaqh7it7uc4cvz2haft7rc6u + '@typescript-eslint/parser': 5.51.0_7kw3g6rralp5ps6mg3uyzz6azm debug: 3.2.7 - eslint: 8.33.0 + eslint: 8.34.0 eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 3.5.3_ohdts44xlqyeyrlje4qnefqeay + eslint-import-resolver-typescript: 3.5.3_mvgyw3chnqkp6sgfmmtihyjpnm transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import/2.27.5_c6pjvta7aysubsdnhvgec47vxe: + /eslint-plugin-import/2.27.5_inmo4nrctlhmfx73hzu6aogupa: resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} engines: {node: '>=4'} peerDependencies: @@ -4046,15 +4699,15 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.49.0_zkdaqh7it7uc4cvz2haft7rc6u + '@typescript-eslint/parser': 5.51.0_7kw3g6rralp5ps6mg3uyzz6azm array-includes: 3.1.6 array.prototype.flat: 1.3.1 array.prototype.flatmap: 1.3.1 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.33.0 + eslint: 8.34.0 eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.7.4_5hqjyc62h4cn6t4zslmr6l4rke + eslint-module-utils: 2.7.4_7d5otxxipfjcaqho6psg7iyp4e has: 1.0.3 is-core-module: 2.11.0 is-glob: 4.0.3 @@ -4069,17 +4722,17 @@ packages: - supports-color dev: true - /eslint-plugin-react-hooks/4.6.0_eslint@8.33.0: + /eslint-plugin-react-hooks/4.6.0_eslint@8.34.0: resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 dependencies: - eslint: 8.33.0 + eslint: 8.34.0 dev: true - /eslint-plugin-react/7.32.1_eslint@8.33.0: - resolution: {integrity: sha512-vOjdgyd0ZHBXNsmvU+785xY8Bfe57EFbTYYk8XrROzWpr9QBvpjITvAXt9xqcE6+8cjR/g1+mfumPToxsl1www==} + /eslint-plugin-react/7.32.2_eslint@8.34.0: + resolution: {integrity: sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==} engines: {node: '>=4'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 @@ -4088,7 +4741,7 @@ packages: array.prototype.flatmap: 1.3.1 array.prototype.tosorted: 1.1.1 doctrine: 2.1.0 - eslint: 8.33.0 + eslint: 8.34.0 estraverse: 5.3.0 jsx-ast-utils: 3.3.3 minimatch: 3.1.2 @@ -4118,13 +4771,13 @@ packages: estraverse: 5.3.0 dev: true - /eslint-utils/3.0.0_eslint@8.33.0: + /eslint-utils/3.0.0_eslint@8.34.0: resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} peerDependencies: eslint: '>=5' dependencies: - eslint: 8.33.0 + eslint: 8.34.0 eslint-visitor-keys: 2.1.0 dev: true @@ -4138,8 +4791,8 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint/8.33.0: - resolution: {integrity: sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==} + /eslint/8.34.0: + resolution: {integrity: sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: @@ -4154,7 +4807,7 @@ packages: doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.1.1 - eslint-utils: 3.0.0_eslint@8.33.0 + eslint-utils: 3.0.0_eslint@8.34.0 eslint-visitor-keys: 3.3.0 espree: 9.4.1 esquery: 1.4.0 @@ -4344,7 +4997,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 es-abstract: 1.21.1 functions-have-names: 1.2.3 dev: true @@ -4394,6 +5047,11 @@ packages: has: 1.0.3 has-symbols: 1.0.3 + /get-nonce/1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + dev: false + /get-own-enumerable-property-symbols/3.0.2: resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} dev: true @@ -4411,8 +5069,8 @@ packages: get-intrinsic: 1.2.0 dev: true - /get-tsconfig/4.3.0: - resolution: {integrity: sha512-YCcF28IqSay3fqpIu5y3Krg/utCBHBeoflkZyHj/QcqI2nrLPC3ZegS9CmIo+hJb8K7aiGsuUl7PwWVjNG2HQQ==} + /get-tsconfig/4.4.0: + resolution: {integrity: sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ==} dev: true /gl-matrix/3.4.3: @@ -4467,7 +5125,7 @@ packages: resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} engines: {node: '>= 0.4'} dependencies: - define-properties: 1.1.4 + define-properties: 1.2.0 dev: true /globalyzer/0.1.0: @@ -4501,12 +5159,6 @@ packages: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} dev: true - /goober/2.1.11: - resolution: {integrity: sha512-5SS2lmxbhqH0u9ABEWq7WPU69a4i2pYcHeCxqaNq6Cw3mnrF0ghWNM4tEGid4dKy8XNIAUbuThuozDHHKJVh3A==} - peerDependencies: - csstype: ^3.0.10 - dev: false - /gopd/1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} dependencies: @@ -4573,18 +5225,6 @@ packages: dependencies: function-bind: 1.1.1 - /html-parse-stringify/3.0.1: - resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} - dependencies: - void-elements: 3.1.0 - dev: false - - /i18next/22.4.9: - resolution: {integrity: sha512-8gWMmUz460KJDQp/ob3MNUX84cVuDRY9PLFPnV8d+Qezz/6dkjxwOaH70xjrCNDO+JrUL25iXfAIN9wUkInNZw==} - dependencies: - '@babel/runtime': 7.20.13 - dev: false - /idb/7.1.1: resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} dev: true @@ -4630,8 +5270,8 @@ packages: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} dev: false - /internal-slot/1.0.4: - resolution: {integrity: sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==} + /internal-slot/1.0.5: + resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} engines: {node: '>= 0.4'} dependencies: get-intrinsic: 1.2.0 @@ -4639,6 +5279,12 @@ packages: side-channel: 1.0.4 dev: true + /invariant/2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + dependencies: + loose-envify: 1.4.0 + dev: false + /is-arguments/1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} @@ -4654,10 +5300,6 @@ packages: is-typed-array: 1.1.10 dev: true - /is-arrayish/0.3.2: - resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - dev: false - /is-bigint/1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} dependencies: @@ -4855,7 +5497,7 @@ packages: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.11.18 + '@types/node': 18.13.0 merge-stream: 2.0.0 supports-color: 7.2.0 dev: true @@ -4909,7 +5551,7 @@ packages: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true dependencies: - minimist: 1.2.7 + minimist: 1.2.8 dev: true /json5/2.2.3: @@ -4961,8 +5603,8 @@ packages: type-check: 0.4.0 dev: true - /libphonenumber-js/1.10.18: - resolution: {integrity: sha512-NS4ZEgNhwbcPz1gfSXCGFnQm0xEiyTSPRthIuWytDzOiEG9xnZ2FbLyfJC4tI2BMAAXpoWbNxHYH75pa3Dq9og==} + /libphonenumber-js/1.10.19: + resolution: {integrity: sha512-MDZ1zLIkfSDZV5xBta3nuvbEOlsnKCPe4z5r3hyup/AXveevkl9A1eSWmLhd2FX4k7pJDe4MrLeQsux0HI/VWg==} dev: false /lilconfig/2.0.6: @@ -4980,10 +5622,6 @@ packages: resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} dev: false - /lodash.curry/4.1.1: - resolution: {integrity: sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==} - dev: false - /lodash.debounce/4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} dev: true @@ -5026,6 +5664,14 @@ packages: yallist: 4.0.0 dev: true + /lucide-react/0.112.0_react@18.2.0: + resolution: {integrity: sha512-+glJwNv7bv6I6WOb8RRbaVHJdaoaV/KWV44ag/EpAoSsWxSUML5RP8hA+9O188RoEv9ZFMx1l+7HPwTDnt0L/Q==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + /magic-string/0.25.9: resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} dependencies: @@ -5047,7 +5693,7 @@ packages: '@mapbox/jsonlint-lines-primitives': 2.0.2 '@mapbox/mapbox-gl-supported': 2.0.1 '@mapbox/point-geometry': 0.1.0 - '@mapbox/tiny-sdf': 2.0.5 + '@mapbox/tiny-sdf': 2.0.6 '@mapbox/unitbezier': 0.0.1 '@mapbox/vector-tile': 1.3.1 '@mapbox/whoots-js': 3.1.0 @@ -5102,8 +5748,8 @@ packages: brace-expansion: 2.0.1 dev: true - /minimist/1.2.7: - resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==} + /minimist/1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} /minipass/3.3.6: resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} @@ -5112,11 +5758,9 @@ packages: yallist: 4.0.0 dev: true - /minipass/4.0.0: - resolution: {integrity: sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==} + /minipass/4.0.3: + resolution: {integrity: sha512-OW2r4sQ0sI+z5ckEt5c1Tri4xTgZwYDxpE54eqWlQloQRoWtXjqt9udJ5Z4dSv7wK+nfFI7FRXyCpBSft+gpFw==} engines: {node: '>=8'} - dependencies: - yallist: 4.0.0 dev: true /minizlib/2.1.2: @@ -5158,8 +5802,8 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /node-releases/2.0.8: - resolution: {integrity: sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==} + /node-releases/2.0.10: + resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} dev: true /normalize-path/3.0.0: @@ -5188,7 +5832,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 /object-keys/1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} @@ -5199,7 +5843,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 has-symbols: 1.0.3 object-keys: 1.1.1 dev: true @@ -5209,7 +5853,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 es-abstract: 1.21.1 dev: true @@ -5218,14 +5862,14 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 es-abstract: 1.21.1 dev: true /object.hasown/1.1.2: resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==} dependencies: - define-properties: 1.1.4 + define-properties: 1.2.0 es-abstract: 1.21.1 dev: true @@ -5234,7 +5878,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 es-abstract: 1.21.1 dev: true @@ -5244,8 +5888,8 @@ packages: wrappy: 1.0.2 dev: true - /open/8.4.0: - resolution: {integrity: sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==} + /open/8.4.1: + resolution: {integrity: sha512-/4b7qZNhv6Uhd7jjnREh1NjnPxlTq+XNWPG88Ydkj5AILcA5m3ajvcg57pB24EQjKv0dK62XnDqk9c/hkIG5Kg==} engines: {node: '>=12'} dependencies: define-lazy-prop: 2.0.0 @@ -5291,11 +5935,6 @@ packages: callsites: 3.1.0 dev: true - /parse-ms/3.0.0: - resolution: {integrity: sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==} - engines: {node: '>=12'} - dev: false - /path-exists/4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -5367,11 +6006,11 @@ packages: read-cache: 1.0.0 resolve: 1.22.1 - /postcss-js/4.0.0_postcss@8.4.21: - resolution: {integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==} + /postcss-js/4.0.1_postcss@8.4.21: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} engines: {node: ^12 || ^14 || >= 16} peerDependencies: - postcss: ^8.3.3 + postcss: ^8.4.21 dependencies: camelcase-css: 2.0.1 postcss: 8.4.21 @@ -5436,7 +6075,7 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /prettier-plugin-tailwindcss/0.2.2_prettier@2.8.3: + /prettier-plugin-tailwindcss/0.2.2_prettier@2.8.4: resolution: {integrity: sha512-5RjUbWRe305pUpc48MosoIp6uxZvZxrM6GyOgsbGLTce+ehePKNm7ziW2dLG2air9aXbGuXlHVSQQw4Lbosq3w==} engines: {node: '>=12.17.0'} peerDependencies: @@ -5485,11 +6124,11 @@ packages: prettier-plugin-twig-melody: optional: true dependencies: - prettier: 2.8.3 + prettier: 2.8.4 dev: true - /prettier/2.8.3: - resolution: {integrity: sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==} + /prettier/2.8.4: + resolution: {integrity: sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==} engines: {node: '>=10.13.0'} hasBin: true dev: true @@ -5499,18 +6138,11 @@ packages: engines: {node: '>=6'} dev: true - /pretty-bytes/6.0.0: - resolution: {integrity: sha512-6UqkYefdogmzqAZWzJ7laYeJnaXDy2/J+ZqiiMtS7t7OfpXWTlaeGMwX8U6EFvPV/YWWEKRkS8hKS4k60WHTOg==} + /pretty-bytes/6.1.0: + resolution: {integrity: sha512-Rk753HI8f4uivXi4ZCIYdhmG1V+WKzvRMg/X+M42a6t7D07RcmopXJMDNk6N++7Bl75URRGsb40ruvg7Hcp2wQ==} engines: {node: ^14.13.1 || >=16.0.0} dev: true - /pretty-ms/8.0.0: - resolution: {integrity: sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==} - engines: {node: '>=14.16'} - dependencies: - parse-ms: 3.0.0 - dev: false - /process-nextick-args/2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} dev: true @@ -5580,28 +6212,6 @@ packages: quickselect: 2.0.0 dev: false - /react-base16-styling/0.9.1: - resolution: {integrity: sha512-1s0CY1zRBOQ5M3T61wetEpvQmsYSNtWEcdYzyZNxKa8t7oDvaOn9d21xrGezGAHFWLM7SHcktPuPTrvoqxSfKw==} - dependencies: - '@babel/runtime': 7.20.13 - '@types/base16': 1.0.2 - '@types/lodash': 4.14.191 - base16: 1.0.0 - color: 3.2.1 - csstype: 3.1.1 - lodash.curry: 4.1.1 - dev: false - - /react-chartjs-2/5.2.0_uxqdnqzacoapcp4mwytn5yclyu: - resolution: {integrity: sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==} - peerDependencies: - chart.js: ^4.1.1 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - chart.js: 4.2.0 - react: 18.2.0 - dev: false - /react-dom/18.2.0_react@18.2.0: resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} peerDependencies: @@ -5612,8 +6222,8 @@ packages: scheduler: 0.23.0 dev: false - /react-hook-form/7.42.1_react@18.2.0: - resolution: {integrity: sha512-2UIGqwMZksd5HS55crTT1ATLTr0rAI4jS7yVuqTaoRVDhY2Qc4IyjskCmpnmdYqUNOYFy04vW253tb2JRVh+IQ==} + /react-hook-form/7.43.1_react@18.2.0: + resolution: {integrity: sha512-+s3+s8LLytRMriwwuSqeLStVjRXFGxgjjx2jED7Z+wz1J/88vpxieRQGvJVvzrzVxshZ0BRuocFERb779m2kNg==} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 @@ -5621,56 +6231,9 @@ packages: react: 18.2.0 dev: false - /react-hot-toast/2.4.0_biqbaboplfbrettd7655fr4n2y: - resolution: {integrity: sha512-qnnVbXropKuwUpriVVosgo8QrB+IaPJCpL8oBI6Ov84uvHZ5QQcTp2qg6ku2wNfgJl6rlQXJIQU5q+5lmPOutA==} - engines: {node: '>=10'} - peerDependencies: - react: '>=16' - react-dom: '>=16' - dependencies: - goober: 2.1.11 - react: 18.2.0 - react-dom: 18.2.0_react@18.2.0 - transitivePeerDependencies: - - csstype - dev: false - - /react-i18next/12.1.4_iakk3dtjhjpukdoa4oua5khgci: - resolution: {integrity: sha512-XQND7jYtgM7ht5PH3yIZljCRpAMTlH/zmngM9ZjToqa+0BR6xuu8c7QF0WIIOEjcMTB2S3iOfpN/xG/ZrAnO6g==} - peerDependencies: - i18next: '>= 19.0.0' - react: '>= 16.8.0' - react-dom: '*' - react-native: '*' - peerDependenciesMeta: - react-dom: - optional: true - react-native: - optional: true - dependencies: - '@babel/runtime': 7.20.13 - html-parse-stringify: 3.0.1 - i18next: 22.4.9 - react: 18.2.0 - react-dom: 18.2.0_react@18.2.0 - dev: false - /react-is/16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - /react-json-tree/0.18.0_3stiutgnnbnfnf3uowm5cip22i: - resolution: {integrity: sha512-Qe6HKSXrr++n9Y31nkRJ3XvQMATISpqigH1vEKhLwB56+nk5thTP0ITThpjxY6ZG/ubpVq/aEHIcyLP/OPHxeA==} - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@babel/runtime': 7.20.13 - '@types/lodash': 4.14.191 - '@types/react': 18.0.27 - react: 18.2.0 - react-base16-styling: 0.9.1 - dev: false - /react-map-gl/7.0.21_6eczaga5xxiwzxtfiyk6fioasq: resolution: {integrity: sha512-Cmokphv6VHfRJsHVjCtn7nw5mf8rl89CHdvPSaif0OWZZqe+pxM2/5hEr4EvPWeTokRPCo1XTrBpGbShkEuktQ==} peerDependencies: @@ -5699,6 +6262,77 @@ packages: engines: {node: '>=0.10.0'} dev: true + /react-remove-scroll-bar/2.3.4_pmekkgnqduwlme35zpnqhenc34: + resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.28 + react: 18.2.0 + react-style-singleton: 2.2.1_pmekkgnqduwlme35zpnqhenc34 + tslib: 2.5.0 + dev: false + + /react-remove-scroll/2.5.4_pmekkgnqduwlme35zpnqhenc34: + resolution: {integrity: sha512-xGVKJJr0SJGQVirVFAUZ2k1QLyO6m+2fy0l8Qawbp5Jgrv3DeLalrfMNBFSlmz5kriGGzsVBtGVnf4pTKIhhWA==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.28 + react: 18.2.0 + react-remove-scroll-bar: 2.3.4_pmekkgnqduwlme35zpnqhenc34 + react-style-singleton: 2.2.1_pmekkgnqduwlme35zpnqhenc34 + tslib: 2.5.0 + use-callback-ref: 1.3.0_pmekkgnqduwlme35zpnqhenc34 + use-sidecar: 1.1.2_pmekkgnqduwlme35zpnqhenc34 + dev: false + + /react-remove-scroll/2.5.5_pmekkgnqduwlme35zpnqhenc34: + resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.28 + react: 18.2.0 + react-remove-scroll-bar: 2.3.4_pmekkgnqduwlme35zpnqhenc34 + react-style-singleton: 2.2.1_pmekkgnqduwlme35zpnqhenc34 + tslib: 2.5.0 + use-callback-ref: 1.3.0_pmekkgnqduwlme35zpnqhenc34 + use-sidecar: 1.1.2_pmekkgnqduwlme35zpnqhenc34 + dev: false + + /react-style-singleton/2.2.1_pmekkgnqduwlme35zpnqhenc34: + resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.28 + get-nonce: 1.0.1 + invariant: 2.2.4 + react: 18.2.0 + tslib: 2.5.0 + dev: false + /react/16.14.0: resolution: {integrity: sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==} engines: {node: '>=0.10.0'} @@ -5772,7 +6406,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 functions-have-names: 1.2.3 /regexpp/3.2.0: @@ -5780,22 +6414,18 @@ packages: engines: {node: '>=8'} dev: true - /regexpu-core/5.2.2: - resolution: {integrity: sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==} + /regexpu-core/5.3.0: + resolution: {integrity: sha512-ZdhUQlng0RoscyW7jADnUZ25F5eVtHdMyXSb2PiwafvteRAOJUjFoUPEYZSIfP99fBIs3maLIRfpEddT78wAAQ==} engines: {node: '>=4'} dependencies: + '@babel/regjsgen': 0.8.0 regenerate: 1.4.2 regenerate-unicode-properties: 10.1.0 - regjsgen: 0.7.1 regjsparser: 0.9.1 unicode-match-property-ecmascript: 2.0.0 unicode-match-property-value-ecmascript: 2.1.0 dev: true - /regjsgen/0.7.1: - resolution: {integrity: sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==} - dev: true - /regjsparser/0.9.1: resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} hasBin: true @@ -5870,7 +6500,7 @@ packages: jest-worker: 26.6.2 rollup: 2.79.1 serialize-javascript: 4.0.0 - terser: 5.16.1 + terser: 5.16.3 dev: true /rollup-plugin-visualizer/5.9.0: @@ -5883,7 +6513,7 @@ packages: rollup: optional: true dependencies: - open: 8.4.0 + open: 8.4.1 picomatch: 2.3.1 source-map: 0.7.4 yargs: 17.6.2 @@ -5897,8 +6527,8 @@ packages: fsevents: 2.3.2 dev: true - /rollup/3.12.0: - resolution: {integrity: sha512-4MZ8kA2HNYahIjz63rzrMMRvDqQDeS9LoriJvMuV0V6zIGysP36e9t4yObUfwdT9h/szXoHQideICftcdZklWg==} + /rollup/3.15.0: + resolution: {integrity: sha512-F9hrCAhnp5/zx/7HYmftvsNBkMfLfk/dXUh73hPSM2E3CRgap65orDNJbLetoiUFwSAk6iHPLvBrZ5iHYvzqsg==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: @@ -5971,12 +6601,6 @@ packages: object-inspect: 1.12.3 dev: true - /simple-swizzle/0.2.2: - resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} - dependencies: - is-arrayish: 0.3.2 - dev: false - /simple-zstd/1.4.2: resolution: {integrity: sha512-kGYEvT33M5XfyQvvW4wxl3eKcWbdbCc1V7OZzuElnaXft0qbVzoIIXHXiCm3JCUki+MZKKmvjl8p2VGLJc5Y/A==} dependencies: @@ -6041,7 +6665,7 @@ packages: resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} engines: {node: '>= 0.4'} dependencies: - internal-slot: 1.0.4 + internal-slot: 1.0.5 dev: true /stream-shift/1.0.1: @@ -6061,11 +6685,11 @@ packages: resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 es-abstract: 1.21.1 get-intrinsic: 1.2.0 has-symbols: 1.0.3 - internal-slot: 1.0.4 + internal-slot: 1.0.5 regexp.prototype.flags: 1.4.3 side-channel: 1.0.4 dev: true @@ -6074,7 +6698,7 @@ packages: resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 es-abstract: 1.21.1 dev: true @@ -6082,7 +6706,7 @@ packages: resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 es-abstract: 1.21.1 dev: true @@ -6166,8 +6790,20 @@ packages: tslib: 2.5.0 dev: true - /tailwindcss/3.2.4_postcss@8.4.21: - resolution: {integrity: sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==} + /tailwind-merge/1.9.1: + resolution: {integrity: sha512-ED9MkiUHlmfh58EC1xHRqXcH1IQyRtmDP0AmXlugYkP2tvfu7ejtjFEHJLJt93mQ7ZJkcqSIgm9M394bq5vOJg==} + dev: false + + /tailwindcss-animate/1.0.5_tailwindcss@3.2.6: + resolution: {integrity: sha512-UU3qrOJ4lFQABY+MVADmBm+0KW3xZyhMdRvejwtXqYOL7YjHYxmuREFAZdmVG5LPe5E9CAst846SLC4j5I3dcw==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + dependencies: + tailwindcss: 3.2.6_postcss@8.4.21 + dev: false + + /tailwindcss/3.2.6_postcss@8.4.21: + resolution: {integrity: sha512-BfgQWZrtqowOQMC2bwaSNe7xcIjdDEgixWGYOd6AL0CbKHJlvhfdbINeAW76l1sO+1ov/MJ93ODJ9yluRituIw==} engines: {node: '>=12.13.0'} hasBin: true peerDependencies: @@ -6189,7 +6825,7 @@ packages: picocolors: 1.0.0 postcss: 8.4.21 postcss-import: 14.1.0_postcss@8.4.21 - postcss-js: 4.0.0_postcss@8.4.21 + postcss-js: 4.0.1_postcss@8.4.21 postcss-load-config: 3.1.4_postcss@8.4.21 postcss-nested: 6.0.0_postcss@8.4.21 postcss-selector-parser: 6.0.11 @@ -6210,7 +6846,7 @@ packages: dependencies: chownr: 2.0.0 fs-minipass: 2.1.0 - minipass: 4.0.0 + minipass: 4.0.3 minizlib: 2.1.2 mkdirp: 1.0.4 yallist: 4.0.0 @@ -6239,8 +6875,8 @@ packages: unique-string: 2.0.0 dev: true - /terser/5.16.1: - resolution: {integrity: sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==} + /terser/5.16.3: + resolution: {integrity: sha512-v8wWLaS/xt3nE9dgKEWhNUFP6q4kngO5B8eYFUuebsu7Dw/UNAnpUod6UHo04jSSkv8TzKHjZDSd7EXdDQAl8Q==} engines: {node: '>=10'} hasBin: true dependencies: @@ -6327,7 +6963,7 @@ packages: dependencies: '@types/json5': 0.0.29 json5: 1.0.2 - minimist: 1.2.7 + minimist: 1.2.8 strip-bom: 3.0.0 dev: true @@ -6337,21 +6973,20 @@ packages: /tslib/2.5.0: resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} - dev: true - /tslog/4.7.1: - resolution: {integrity: sha512-Ez90j4FKCUp9bBlgPq96aYDUbXRIOxz6Vxn/4Iw2/IiVxLB5wsUVkWfeK4oqdRMoW8qBVJz9oIT+ysjfyIRufw==} + /tslog/4.7.2: + resolution: {integrity: sha512-NZCunFmbQK25tt+Egv28MLcmbo8M1HgUy6X2hdVbgrAlcR7zRGvPmM8SnpoljXZ48zHRRYWp9vYIHFHKWsR4HA==} engines: {node: '>=16'} dev: false - /tsutils/3.21.0_typescript@4.9.4: + /tsutils/3.21.0_typescript@4.9.5: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' dependencies: tslib: 1.14.1 - typescript: 4.9.4 + typescript: 4.9.5 dev: true /turf-jsts/1.2.3: @@ -6383,11 +7018,10 @@ packages: is-typed-array: 1.1.10 dev: true - /typescript/4.9.4: - resolution: {integrity: sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==} + /typescript/4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} engines: {node: '>=4.2.0'} hasBin: true - dev: true /unbox-primitive/1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} @@ -6438,13 +7072,13 @@ packages: engines: {node: '>=4'} dev: true - /update-browserslist-db/1.0.10_browserslist@4.21.4: + /update-browserslist-db/1.0.10_browserslist@4.21.5: resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' dependencies: - browserslist: 4.21.4 + browserslist: 4.21.5 escalade: 3.1.1 picocolors: 1.0.0 dev: true @@ -6455,6 +7089,50 @@ packages: punycode: 2.3.0 dev: true + /use-callback-ref/1.3.0_pmekkgnqduwlme35zpnqhenc34: + resolution: {integrity: sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.28 + react: 18.2.0 + tslib: 2.5.0 + dev: false + + /use-isomorphic-layout-effect/1.1.2_pmekkgnqduwlme35zpnqhenc34: + resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.28 + react: 18.2.0 + dev: false + + /use-sidecar/1.1.2_pmekkgnqduwlme35zpnqhenc34: + resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 18.0.28 + detect-node-es: 1.1.0 + react: 18.2.0 + tslib: 2.5.0 + dev: false + /use-sync-external-store/1.2.0_react@18.2.0: resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} peerDependencies: @@ -6476,30 +7154,30 @@ packages: hasBin: true dev: true - /validator/13.7.0: - resolution: {integrity: sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==} + /validator/13.9.0: + resolution: {integrity: sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==} engines: {node: '>= 0.10'} dev: false - /vite-plugin-environment/1.1.3_vite@4.0.4: + /vite-plugin-environment/1.1.3_vite@4.1.1: resolution: {integrity: sha512-9LBhB0lx+2lXVBEWxFZC+WO7PKEyE/ykJ7EPWCq95NEcCpblxamTbs5Dm3DLBGzwODpJMEnzQywJU8fw6XGGGA==} peerDependencies: vite: '>= 2.7' dependencies: - vite: 4.0.4_@types+node@18.11.18 + vite: 4.1.1_@types+node@18.13.0 dev: true - /vite-plugin-pwa/0.14.1_vite@4.0.4: - resolution: {integrity: sha512-5zx7yhQ8RTLwV71+GA9YsQQ63ALKG8XXIMqRJDdZkR8ZYftFcRgnzM7wOWmQZ/DATspyhPih5wCdcZnAIsM+mA==} + /vite-plugin-pwa/0.14.4_vite@4.1.1: + resolution: {integrity: sha512-M7Ct0so8OlouMkTWgXnl8W1xU95glITSKIe7qswZf1tniAstO2idElGCnsrTJ5NPNSx1XqfTCOUj8j94S6FD7Q==} peerDependencies: vite: ^3.1.0 || ^4.0.0 dependencies: - '@rollup/plugin-replace': 5.0.2_rollup@3.12.0 + '@rollup/plugin-replace': 5.0.2_rollup@3.15.0 debug: 4.3.4 fast-glob: 3.2.12 - pretty-bytes: 6.0.0 - rollup: 3.12.0 - vite: 4.0.4_@types+node@18.11.18 + pretty-bytes: 6.1.0 + rollup: 3.15.0 + vite: 4.1.1_@types+node@18.13.0 workbox-build: 6.5.4 workbox-window: 6.5.4 transitivePeerDependencies: @@ -6507,8 +7185,8 @@ packages: - supports-color dev: true - /vite/4.0.4_@types+node@18.11.18: - resolution: {integrity: sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==} + /vite/4.1.1_@types+node@18.13.0: + resolution: {integrity: sha512-LM9WWea8vsxhr782r9ntg+bhSFS06FJgCvvB0+8hf8UWtvaiDagKYWXndjfX6kGl74keHJUcpzrQliDXZlF5yg==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true peerDependencies: @@ -6532,20 +7210,15 @@ packages: terser: optional: true dependencies: - '@types/node': 18.11.18 + '@types/node': 18.13.0 esbuild: 0.16.17 postcss: 8.4.21 resolve: 1.22.1 - rollup: 3.12.0 + rollup: 3.15.0 optionalDependencies: fsevents: 2.3.2 dev: true - /void-elements/3.1.0: - resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} - engines: {node: '>=0.10.0'} - dev: false - /vt-pbf/3.1.3: resolution: {integrity: sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==} dependencies: @@ -6820,8 +7493,8 @@ packages: engines: {node: '>=10'} dev: true - /zustand/4.3.2_immer@9.0.19+react@18.2.0: - resolution: {integrity: sha512-rd4haDmlwMTVWVqwvgy00ny8rtti/klRoZjFbL/MAcDnmD5qSw/RZc+Vddstdv90M5Lv6RPgWvm1Hivyn0QgJw==} + /zustand/4.3.3_immer@9.0.19+react@18.2.0: + resolution: {integrity: sha512-x2jXq8S0kfLGNwGh87nhRfEc2eZy37tSatpSoSIN+O6HIaBhgQHSONV/F9VNrNcBcKQu/E80K1DeHDYQC/zCrQ==} engines: {node: '>=12.7.0'} peerDependencies: immer: '>=9.0' diff --git a/src/App.tsx b/src/App.tsx index 22d991d0..2f91c9c3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,41 +2,45 @@ import { MapProvider } from "react-map-gl"; import { useAppStore } from "@core/stores/appStore.js"; import { DeviceWrapper } from "@app/DeviceWrapper.js"; import { PageRouter } from "@app/PageRouter.js"; -import { CommandPalette } from "@components/CommandPalette/Index.js"; +import { CommandPalette } from "@components/CommandPalette.js"; import { DeviceSelector } from "@components/DeviceSelector.js"; import { DialogManager } from "@components/Dialog/DialogManager.js"; -import { NewDevice } from "@components/NewDevice.js"; -import { Sidebar } from "@components/Sidebar.js"; +import { Dashboard } from "@app/components/Dashboard.js"; import { useDeviceStore } from "@core/stores/deviceStore.js"; - -import { Drawer } from "@components/Drawer/index.js"; import { ThemeController } from "@components/generic/ThemeController.js"; -import { BottomNav } from "@app/Nav/BottomNav.js"; +import { NewDeviceDialog } from "./components/Dialog/NewDevice.js"; export const App = (): JSX.Element => { const { getDevice } = useDeviceStore(); - const { selectedDevice, darkMode, accent } = useAppStore(); + const { selectedDevice, setConnectDialogOpen, connectDialogOpen } = + useAppStore(); const device = getDevice(selectedDevice); return ( + { + setConnectDialogOpen(open); + }} + /> -
- -
- {device ? ( -
- - - - -
- ) : ( - - )} - {device && } +
+
+ +
+ {device ? ( +
+ + + +
+ ) : ( + + )} +
diff --git a/src/DeviceWrapper.tsx b/src/DeviceWrapper.tsx index 311186d2..8681ab47 100644 --- a/src/DeviceWrapper.tsx +++ b/src/DeviceWrapper.tsx @@ -1,4 +1,4 @@ -import { DeviceContext } from "@core/providers/useDevice.js"; +import { DeviceContext } from "@core/stores/deviceStore.js"; import type { Device } from "@core/stores/deviceStore.js"; import type { ReactNode } from "react"; diff --git a/src/Nav/BottomNav.tsx b/src/Nav/BottomNav.tsx deleted file mode 100644 index f0a30dfe..00000000 --- a/src/Nav/BottomNav.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { GitBranchIcon } from "@primer/octicons-react"; -import type { ReactNode } from "react"; - -export interface BottomNavProps { - children: ReactNode; -} - -export const BottomNav = ({ children }: BottomNavProps): JSX.Element => { - return ( -
-
- - - {process.env.COMMIT_HASH} - -
- {children} -
- ); -}; diff --git a/src/Nav/NavBar.tsx b/src/Nav/NavBar.tsx deleted file mode 100644 index 7c5b1687..00000000 --- a/src/Nav/NavBar.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Button } from "@app/components/form/Button.js"; -import { ChevronRightIcon, HomeIcon } from "@primer/octicons-react"; - -export interface NavBarProps { - breadcrumb: string[]; - actions?: { - label: string; - onClick: () => void; - }[]; -} - -export const NavBar = ({ breadcrumb, actions }: NavBarProps): JSX.Element => { - return ( -
-
    -
  1. - -
  2. - {breadcrumb.map((breadcrumb, index) => ( -
  3. - - - {breadcrumb} - -
  4. - ))} -
-
- {actions?.map((Action, index) => ( - - ))} -
-
- ); -}; diff --git a/src/Nav/NavSpacer.tsx b/src/Nav/NavSpacer.tsx deleted file mode 100755 index f794cec0..00000000 --- a/src/Nav/NavSpacer.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export const NavSpacer = (): JSX.Element => { - return
; -}; diff --git a/src/Nav/PageNav.tsx b/src/Nav/PageNav.tsx deleted file mode 100755 index 25fd0848..00000000 --- a/src/Nav/PageNav.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import type { ComponentType, SVGProps } from "react"; -import { useDevice } from "@core/providers/useDevice.js"; -import type { Page } from "@core/stores/deviceStore.js"; -import { - BeakerIcon, - ChatBubbleBottomCenterTextIcon, - Cog8ToothIcon, - MapIcon, - Square3Stack3DIcon, - UsersIcon -} from "@heroicons/react/24/outline"; - -export const PageNav = (): JSX.Element => { - const { activePage, setActivePage } = useDevice(); - - interface NavLink { - name: string; - icon: ComponentType>; - page: Page; - } - - const pages: NavLink[] = [ - { - name: "Messages", - icon: ChatBubbleBottomCenterTextIcon, - page: "messages" - }, - { - name: "Map", - icon: MapIcon, - page: "map" - }, - { - name: "Config", - icon: Cog8ToothIcon, - page: "config" - }, - { - name: "Channels", - icon: Square3Stack3DIcon, - page: "channels" - }, - { - name: "Peers", - icon: UsersIcon, - page: "peers" - } - ]; - - return ( -
- {pages.map((Link) => ( -
{ - setActivePage(Link.page); - }} - className={`border-x-4 border-backgroundPrimary bg-backgroundPrimary py-5 px-4 hover:brightness-hover active:brightness-press ${ - Link.page === activePage - ? "border-l-accent text-textPrimary" - : "text-textSecondary hover:text-textPrimary" - }`} - > - -
- ))} -
- ); -}; diff --git a/src/PageRouter.tsx b/src/PageRouter.tsx index 9fe5d13c..b5033460 100644 --- a/src/PageRouter.tsx +++ b/src/PageRouter.tsx @@ -1,4 +1,4 @@ -import { useDevice } from "@core/providers/useDevice.js"; +import { useDevice } from "@core/stores/deviceStore.js"; import { ChannelsPage } from "@pages/Channels.js"; import { ConfigPage } from "@pages/Config/index.js"; import { MapPage } from "@pages/Map.js"; @@ -8,12 +8,12 @@ import { PeersPage } from "@pages/Peers.js"; export const PageRouter = (): JSX.Element => { const { activePage } = useDevice(); return ( -
+ <> {activePage === "messages" && } {activePage === "map" && } {activePage === "config" && } {activePage === "channels" && } {activePage === "peers" && } -
+ ); }; diff --git a/src/components/CommandPalette/Index.tsx b/src/components/CommandPalette.tsx similarity index 56% rename from src/components/CommandPalette/Index.tsx rename to src/components/CommandPalette.tsx index 7c88f36c..b53b1df8 100644 --- a/src/components/CommandPalette/Index.tsx +++ b/src/components/CommandPalette.tsx @@ -1,52 +1,49 @@ -import { ComponentType, Fragment, SVGProps, useEffect, useState } from "react"; -import { toast } from "react-hot-toast"; -import { useDevice } from "@core/providers/useDevice.js"; +import { useEffect } from "react"; import { useAppStore } from "@core/stores/appStore.js"; -import { useDeviceStore } from "@core/stores/deviceStore.js"; -import { GroupView } from "@components/CommandPalette/GroupView.js"; -import { NoResults } from "@components/CommandPalette/NoResults.js"; -import { PaletteTransition } from "@components/CommandPalette/PaletteTransition.js"; -import { SearchBox } from "@components/CommandPalette/SearchBox.js"; -import { SearchResult } from "@components/CommandPalette/SearchResult.js"; +import { useDevice, useDeviceStore } from "@core/stores/deviceStore.js"; +import { useCommandState } from "cmdk"; import { Hashicon } from "@emeraldpay/hashicon-react"; -import { Combobox, Dialog, Transition } from "@headlessui/react"; import { - ArchiveBoxXMarkIcon, - ArrowDownOnSquareStackIcon, - ArrowPathIcon, - ArrowPathRoundedSquareIcon, - ArrowsRightLeftIcon, - BeakerIcon, - BugAntIcon, - Cog8ToothIcon, - CubeTransparentIcon, - DevicePhoneMobileIcon, - InboxIcon, + LucideIcon, LinkIcon, + TrashIcon, MapIcon, MoonIcon, PlusIcon, PowerIcon, + EraserIcon, + RefreshCwIcon, + FactoryIcon, + ArrowLeftRightIcon, + BugIcon, + SettingsIcon, + SmartphoneIcon, + MessageSquareIcon, QrCodeIcon, - QueueListIcon, - Square3Stack3DIcon, - SwatchIcon, - TrashIcon, + LayersIcon, + PaletteIcon, UsersIcon, - WindowIcon, - XCircleIcon -} from "@heroicons/react/24/outline"; -import { Blur } from "@components/generic/Blur.js"; -import { ThemeController } from "@components/generic/ThemeController.js"; + LayoutIcon, + XCircleIcon, + BoxSelectIcon +} from "lucide-react"; +import { + CommandDialog, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList +} from "@components/UI/Command.js"; export interface Group { label: string; - icon: ComponentType>; + icon: LucideIcon; commands: Command[]; } export interface Command { label: string; - icon: ComponentType>; + icon: LucideIcon; action?: () => void; subItems?: SubItem[]; tags?: string[]; @@ -59,11 +56,9 @@ export interface SubItem { } export const CommandPalette = (): JSX.Element => { - const [query, setQuery] = useState(""); const { commandPaletteOpen, setCommandPaletteOpen, - devices, setSelectedDevice, removeDevice, selectedDevice, @@ -72,7 +67,6 @@ export const CommandPalette = (): JSX.Element => { setAccent } = useAppStore(); const { getDevices } = useDeviceStore(); - const { setDialogOpen, setActivePage, connection } = useDevice(); const groups: Group[] = [ @@ -82,7 +76,7 @@ export const CommandPalette = (): JSX.Element => { commands: [ { label: "Messages", - icon: InboxIcon, + icon: MessageSquareIcon, action() { setActivePage("messages"); } @@ -96,7 +90,7 @@ export const CommandPalette = (): JSX.Element => { }, { label: "Config", - icon: Cog8ToothIcon, + icon: SettingsIcon, action() { setActivePage("config"); }, @@ -104,7 +98,7 @@ export const CommandPalette = (): JSX.Element => { }, { label: "Channels", - icon: Square3Stack3DIcon, + icon: LayersIcon, action() { setActivePage("channels"); } @@ -120,11 +114,11 @@ export const CommandPalette = (): JSX.Element => { }, { label: "Manage", - icon: DevicePhoneMobileIcon, + icon: SmartphoneIcon, commands: [ { label: "Switch Node", - icon: ArrowsRightLeftIcon, + icon: ArrowLeftRightIcon, subItems: getDevices().map((device) => { return { label: @@ -133,7 +127,7 @@ export const CommandPalette = (): JSX.Element => { )?.data.user?.longName ?? device.hardware.myNodeNum.toString(), icon: ( ), @@ -154,7 +148,7 @@ export const CommandPalette = (): JSX.Element => { }, { label: "Contextual", - icon: CubeTransparentIcon, + icon: BoxSelectIcon, commands: [ { label: "QR Code", @@ -162,14 +156,14 @@ export const CommandPalette = (): JSX.Element => { subItems: [ { label: "Generator", - icon: , + icon: , action() { setDialogOpen("QR", true); } }, { label: "Import", - icon: , + icon: , action() { setDialogOpen("import", true); } @@ -194,7 +188,7 @@ export const CommandPalette = (): JSX.Element => { }, { label: "Schedule Reboot", - icon: ArrowPathIcon, + icon: RefreshCwIcon, action() { setDialogOpen("reboot", true); } @@ -203,44 +197,32 @@ export const CommandPalette = (): JSX.Element => { label: "Reset Peers", icon: TrashIcon, action() { - if (connection) { - void toast.promise(connection.resetPeers(), { - loading: "Resetting...", - success: "Succesfully reset peers", - error: "No response received" - }); - } + connection?.resetPeers(); } }, { label: "Factory Reset", - icon: ArrowPathRoundedSquareIcon, + icon: FactoryIcon, action() { - if (connection) { - void toast.promise(connection.factoryReset(), { - loading: "Resetting...", - success: "Succesfully factory peers", - error: "No response received" - }); - } + connection?.factoryReset(); } } ] }, { label: "Debug", - icon: BugAntIcon, + icon: BugIcon, commands: [ { label: "Reconfigure", - icon: ArrowPathIcon, + icon: RefreshCwIcon, action() { void connection?.configure(); } }, { label: "[WIP] Clear Messages", - icon: ArchiveBoxXMarkIcon, + icon: EraserIcon, action() { alert("This feature is not implemented"); } @@ -249,7 +231,7 @@ export const CommandPalette = (): JSX.Element => { }, { label: "Application", - icon: WindowIcon, + icon: LayoutIcon, commands: [ { label: "Toggle Dark Mode", @@ -260,7 +242,7 @@ export const CommandPalette = (): JSX.Element => { }, { label: "Accent Color", - icon: SwatchIcon, + icon: PaletteIcon, subItems: [ { label: "Red", @@ -359,102 +341,72 @@ export const CommandPalette = (): JSX.Element => { } ]; - const handleKeydown = (e: KeyboardEvent) => { - if (e.key === "k" && (e.metaKey || e.ctrlKey)) { - e.preventDefault(); - setCommandPaletteOpen(true); - } - }; - useEffect(() => { + const handleKeydown = (e: KeyboardEvent) => { + if (e.key === "k" && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + setCommandPaletteOpen(true); + } + }; + window.addEventListener("keydown", handleKeydown); return () => window.removeEventListener("keydown", handleKeydown); }, []); - const filtered = - query === "" - ? [] - : groups - .map((group) => { - return { - ...group, - commands: group.commands.filter((command) => { - const nameIncludes = `${group.label} ${command.label}` - .toLowerCase() - .includes(query.toLowerCase()); - - const tagsInclude = ( - command.tags - ?.map((t) => t.includes(query.toLowerCase())) - .filter(Boolean) ?? [] - ).length; - - const subItemsInclude = ( - command.subItems - ?.map((s) => - s.label.toLowerCase().includes(query.toLowerCase()) - ) - .filter(Boolean) ?? [] - ).length; - return nameIncludes || tagsInclude || subItemsInclude; - }) - }; - }) - .filter((group) => group.commands.length); - return ( - setQuery("")} - appear + - - - - - - - onChange={(input) => { - if (typeof input === "string") { - setQuery(input); - } else if (input.action) { + + + No results found. + {groups.map((group) => ( + + {group.commands.map((command) => ( + <> + { + command.action && command.action(); setCommandPaletteOpen(false); - input.action(); - } - }} - > - - - {query === "" || filtered.length > 0 ? ( - -
  • -
      - {filtered.map((group, index) => ( - - ))} - {query === "" && - groups.map((group, index) => ( - - ))} -
    -
  • -
    - ) : ( - query !== "" && filtered.length === 0 && - )} - -
    -
    -
    -
    -
    + }} + > + + {command.label} + + {command.subItems && + command.subItems.map((subItem) => ( + + ))} + + ))} + + ))} + + + ); +}; + +const SubItem = ({ + label, + icon, + action +}: { + label: string; + icon: React.ReactNode; + action: () => void; +}) => { + const search = useCommandState((state) => state.search); + if (!search) return null; + + return ( + + {icon} + {label} + ); }; diff --git a/src/components/CommandPalette/GroupView.tsx b/src/components/CommandPalette/GroupView.tsx deleted file mode 100644 index ec1014c9..00000000 --- a/src/components/CommandPalette/GroupView.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import type { Group } from "@components/CommandPalette/Index.js"; -import { Combobox } from "@headlessui/react"; -import { ChevronRightIcon } from "@heroicons/react/24/outline"; - -export interface GroupViewProps { - group: Group; -} - -export const GroupView = ({ group }: GroupViewProps): JSX.Element => { - return ( - - `flex cursor-default select-none items-center rounded-md px-3 py-2 ${ - active ? "bg-backgroundPrimary text-textPrimary" : "" - }` - } - > - {({ active }) => ( - <> - - {group.label} - {active && } - - )} - - ); -}; diff --git a/src/components/CommandPalette/NoResults.tsx b/src/components/CommandPalette/NoResults.tsx deleted file mode 100644 index b712f075..00000000 --- a/src/components/CommandPalette/NoResults.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { Mono } from "@components/generic/Mono.js"; -import { CommandLineIcon } from "@heroicons/react/24/outline"; - -export const NoResults = (): JSX.Element => { - return ( -
    - - - Query does not match any avaliable commands - -
    - ); -}; diff --git a/src/components/CommandPalette/PaletteTransition.tsx b/src/components/CommandPalette/PaletteTransition.tsx deleted file mode 100644 index edb64158..00000000 --- a/src/components/CommandPalette/PaletteTransition.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Fragment, ReactNode } from "react"; -import { Transition } from "@headlessui/react"; - -export interface PaletteTransitionProps { - children: ReactNode; -} - -export const PaletteTransition = ({ - children -}: PaletteTransitionProps): JSX.Element => { - return ( - <> - -
    - - -
    - - {children} - -
    - - ); -}; diff --git a/src/components/CommandPalette/SearchBox.tsx b/src/components/CommandPalette/SearchBox.tsx deleted file mode 100644 index 17d08db2..00000000 --- a/src/components/CommandPalette/SearchBox.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Combobox } from "@headlessui/react"; -import { MagnifyingGlassIcon } from "@heroicons/react/24/outline"; - -export interface SearchBoxProps { - setQuery: (query: string) => void; -} - -export const SearchBox = ({ setQuery }: SearchBoxProps): JSX.Element => { - return ( -
    - - setQuery(event.target.value)} - /> -
    - ); -}; diff --git a/src/components/CommandPalette/SearchResult.tsx b/src/components/CommandPalette/SearchResult.tsx deleted file mode 100644 index cd6a6b01..00000000 --- a/src/components/CommandPalette/SearchResult.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import type { Group } from "@components/CommandPalette/Index.js"; -import { Combobox } from "@headlessui/react"; -import { ChevronRightIcon } from "@heroicons/react/24/outline"; - -export interface SearchResultProps { - group: Group; -} - -export const SearchResult = ({ group }: SearchResultProps): JSX.Element => { - return ( -
    -
    - - {group.label} -
    - {group.commands.map((command, index) => ( -
    - - `mr-2 ml-4 flex cursor-pointer select-none items-center rounded-md px-3 py-1 ${ - active ? "text-gray-900 bg-backgroundPrimary" : "" - }` - } - > - {({ active }) => ( - <> - - {command.label} - {active && ( - - )} - - )} - - {command.subItems && ( -
    - {command.subItems?.map((item, index) => ( - - `mx-2 flex cursor-pointer select-none items-center rounded-md px-3 py-1 ${ - active - ? "text-gray-900 bg-backgroundPrimary bg-opacity-5" - : "" - }` - } - > - {({ active }) => ( - <> - {item.icon} - {item.label} - {active && ( - - )} - - )} - - ))} -
    - )} -
    - ))} -
    - ); -}; diff --git a/src/components/Dashboard.tsx b/src/components/Dashboard.tsx new file mode 100644 index 00000000..c6afe68c --- /dev/null +++ b/src/components/Dashboard.tsx @@ -0,0 +1,104 @@ +import { useAppStore } from "@app/core/stores/appStore.js"; +import { Button } from "@components/UI/Button.js"; +import { + PlusIcon, + ListPlusIcon, + UsersIcon, + MapPinIcon, + CalendarIcon, + BluetoothIcon, + UsbIcon, + NetworkIcon +} from "lucide-react"; +import { Subtle } from "./UI/Typography/Subtle.js"; +import { H3 } from "./UI/Typography/H3.js"; +import { useDeviceStore } from "@app/core/stores/deviceStore.js"; +import { useMemo } from "react"; +import { Separator } from "./UI/Seperator.js"; + +export const Dashboard = () => { + const { setConnectDialogOpen } = useAppStore(); + const { getDevices } = useDeviceStore(); + + const devices = useMemo(() => getDevices(), [getDevices]); + + return ( +
    +
    +
    +

    Connected Devices

    + Manage, connect and disconnect devices +
    +
    + + + +
    + {devices.length ? ( +
      + {devices.map((device) => { + return ( +
    • +
      +
      +

      + {device.nodes.filter( + (n) => n.data.num === device.hardware.myNodeNum + )[0]?.data.user?.longName ?? "UNK"} +

      +
      + {device.connection?.connType === "ble" && ( + <> + + BLE + + )} + {device.connection?.connType === "serial" && ( + <> + + Serial + + )} + {device.connection?.connType === "http" && ( + <> + + Network + + )} +
      +
      +
      +
      +
      +
      +
      +
    • + ); + })} +
    + ) : ( +
    + +

    No Devices

    + Connect atleast one device to get started + +
    + )} +
    +
    + ); +}; diff --git a/src/components/DeviceSelector.tsx b/src/components/DeviceSelector.tsx index 7e00624b..2d2baec9 100644 --- a/src/components/DeviceSelector.tsx +++ b/src/components/DeviceSelector.tsx @@ -1,69 +1,84 @@ import { useAppStore } from "@core/stores/appStore.js"; import { useDeviceStore } from "@core/stores/deviceStore.js"; -import { NavSpacer } from "@app/Nav/NavSpacer.js"; -import { PageNav } from "@app/Nav/PageNav.js"; import { Hashicon } from "@emeraldpay/hashicon-react"; -import { PlusIcon } from "@heroicons/react/24/outline"; -import { MoonIcon, SunIcon } from "@primer/octicons-react"; +import { + PlusIcon, + HomeIcon, + LanguagesIcon, + SunIcon, + MoonIcon, + GithubIcon, + TerminalIcon +} from "lucide-react"; +import { Separator } from "./UI/Seperator.js"; +import { Code } from "./UI/Typography/Code.js"; +import { DeviceSelectorButton } from "./DeviceSelectorButton.js"; export const DeviceSelector = (): JSX.Element => { const { getDevices } = useDeviceStore(); - const { selectedDevice, setSelectedDevice, darkMode, setDarkMode } = - useAppStore(); + const { + selectedDevice, + setSelectedDevice, + darkMode, + setDarkMode, + setCommandPaletteOpen, + setConnectDialogOpen + } = useAppStore(); return ( -
    -
    - + ); }; diff --git a/src/components/DeviceSelectorButton.tsx b/src/components/DeviceSelectorButton.tsx new file mode 100644 index 00000000..37f46981 --- /dev/null +++ b/src/components/DeviceSelectorButton.tsx @@ -0,0 +1,22 @@ +import { cn } from "@app/core/utils/cn.js"; + +export interface DeviceSelectorButtonProps { + active: boolean; + onClick: () => void; + children?: React.ReactNode; +} + +export const DeviceSelectorButton = ({ + active, + onClick, + children +}: DeviceSelectorButtonProps): JSX.Element => ( +
  • + {active && ( +
    + )} +
    + {children} +
    +
  • +); diff --git a/src/components/Dialog/DeviceNameDialog.tsx b/src/components/Dialog/DeviceNameDialog.tsx new file mode 100644 index 00000000..c13efc74 --- /dev/null +++ b/src/components/Dialog/DeviceNameDialog.tsx @@ -0,0 +1,74 @@ +import { Input } from "@components/UI/Input.js"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle +} from "@components/UI/Dialog.js"; +import { Button } from "@components/UI/Button.js"; +import { useDevice } from "@app/core/stores/deviceStore.js"; +import { useForm } from "react-hook-form"; +import { Protobuf } from "@meshtastic/meshtasticjs"; +import { Label } from "../UI/Label.js"; + +export interface User { + longName: string; + shortName: string; +} + +export interface DeviceNameDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; +} + +export const DeviceNameDialog = ({ + open, + onOpenChange +}: DeviceNameDialogProps): JSX.Element => { + const { hardware, nodes, connection, setDialogOpen } = useDevice(); + + const myNode = nodes.find((n) => n.data.num === hardware.myNodeNum); + + const { register, handleSubmit } = useForm({ + values: { + longName: myNode?.data.user?.longName ?? "Unknown", + shortName: myNode?.data.user?.shortName ?? "Unknown" + } + }); + + const onSubmit = handleSubmit((data) => { + connection?.setOwner( + new Protobuf.User({ + ...myNode?.data.user, + ...data + }) + ); + setDialogOpen("deviceName", false); + }); + + return ( + + + + Change Device Name + + The Device will restart once the config is saved. + + +
    +
    + + + + +
    +
    + + + +
    +
    + ); +}; diff --git a/src/components/Dialog/DialogManager.tsx b/src/components/Dialog/DialogManager.tsx index 712c8b4e..8e933deb 100644 --- a/src/components/Dialog/DialogManager.tsx +++ b/src/components/Dialog/DialogManager.tsx @@ -1,41 +1,48 @@ -import { useDevice } from "@core/providers/useDevice.js"; +import { useDevice } from "@core/stores/deviceStore.js"; import { QRDialog } from "@components/Dialog/QRDialog.js"; import { RebootDialog } from "@components/Dialog/RebootDialog.js"; import { ShutdownDialog } from "@components/Dialog/ShutdownDialog.js"; import { ImportDialog } from "@components/Dialog/ImportDialog.js"; +import { DeviceNameDialog } from "./DeviceNameDialog.js"; export const DialogManager = (): JSX.Element => { const { channels, config, dialog, setDialogOpen } = useDevice(); return ( <> { - setDialogOpen("QR", false); + open={dialog.QR} + onOpenChange={(open) => { + setDialogOpen("QR", open); }} channels={channels.map((ch) => ch.config)} loraConfig={config.lora} /> { - setDialogOpen("import", false); + open={dialog.import} + onOpenChange={(open) => { + setDialogOpen("import", open); }} channels={channels.map((ch) => ch.config)} loraConfig={config.lora} /> { + open={dialog.shutdown} + onOpenChange={() => { setDialogOpen("shutdown", false); }} /> { + open={dialog.reboot} + onOpenChange={() => { setDialogOpen("reboot", false); }} /> + { + setDialogOpen("deviceName", open); + }} + /> ); }; diff --git a/src/components/Dialog/ImportDialog.tsx b/src/components/Dialog/ImportDialog.tsx index db3d6ceb..72490985 100644 --- a/src/components/Dialog/ImportDialog.tsx +++ b/src/components/Dialog/ImportDialog.tsx @@ -2,24 +2,29 @@ import { useEffect, useState } from "react"; import { toByteArray } from "base64-js"; import { Checkbox } from "@components/form/Checkbox.js"; import { Input } from "@components/form/Input.js"; -import { Dialog } from "@components/generic/Dialog.js"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle +} from "@components/UI/Dialog.js"; import { Protobuf } from "@meshtastic/meshtasticjs"; -import { Select } from "@components/form/Select.js"; -import { renderOptions } from "@core/utils/selectEnumOptions.js"; import { Toggle } from "@components/form/Toggle.js"; -import { Button } from "@components/form/Button.js"; -import { useDevice } from "@core/providers/useDevice.js"; +import { Button } from "@components/UI/Button.js"; +import { useDevice } from "@core/stores/deviceStore.js"; export interface ImportDialogProps { - isOpen: boolean; - close: () => void; + open: boolean; + onOpenChange: (open: boolean) => void; loraConfig?: Protobuf.Config_LoRaConfig; channels: Protobuf.Channel[]; } export const ImportDialog = ({ - isOpen, - close + open, + onOpenChange }: ImportDialogProps): JSX.Element => { const [QRCodeURL, setQRCodeURL] = useState(""); const [channelSet, setChannelSet] = useState(); @@ -68,69 +73,73 @@ export const ImportDialog = ({ }; return ( - -
    - { - setQRCodeURL(e.target.value); - }} - /> - {validURL && ( -
    -
    -
    - + + + Import Channel Set + + The current LoRa configuration will be overridden. + + +
    + { + setQRCodeURL(e.target.value); + }} + /> + {validURL && ( +
    +
    +
    + +
    + {/* */}
    - -
    - + {renderOptions(Protobuf.Config_LoRaConfig_RegionCode)} + */} - - Channels: - -
    - {channelSet?.settings.map((channel, index) => ( - - ))} + + Channels: + +
    + {channelSet?.settings.map((channel, index) => ( + + ))} +
    -
    - )} - -
    + )} +
    + + + +
    ); }; diff --git a/src/components/Dialog/NewDevice.tsx b/src/components/Dialog/NewDevice.tsx new file mode 100644 index 00000000..8a25ef1b --- /dev/null +++ b/src/components/Dialog/NewDevice.tsx @@ -0,0 +1,97 @@ +import { Input } from "@components/form/Input.js"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle +} from "@components/UI/Dialog.js"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "../UI/Tabs.js"; +import { Subtle } from "../UI/Typography/Subtle.js"; +import { Link } from "../UI/Typography/Link.js"; +import { HTTP } from "../PageComponents/Connect/HTTP.js"; +import { BLE } from "../PageComponents/Connect/BLE.js"; +import { Serial } from "../PageComponents/Connect/Serial.js"; + +const tabs = [ + { + label: "HTTP", + element: HTTP, + disabled: false, + disabledMessage: "Unsuported connection method" + }, + { + label: "Bluetooth", + element: BLE, + disabled: !navigator.bluetooth, + disabledMessage: + "Web Bluetooth is currently only supported by Chromium-based browsers", + disabledLink: + "https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API#browser_compatibility" + }, + { + label: "Serial", + element: Serial, + disabled: !navigator.serial, + disabledMessage: + "WebSerial is currently only supported by Chromium based browsers: https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API#browser_compatibility" + } +]; +export interface NewDeviceProps { + open: boolean; + onOpenChange: (open: boolean) => void; +} + +export const NewDeviceDialog = ({ + open, + onOpenChange +}: NewDeviceProps): JSX.Element => { + return ( + + + + Connect New Device + + + + {tabs.map((tab) => ( + + {tab.label} + + ))} + + {tabs.map((tab) => ( + + {tab.disabled ? ( +

    + {tab.disabledMessage} +

    + ) : ( + + )} +
    + ))} +
    + + {(!navigator.bluetooth || !navigator.serial) && ( + <> + + Web Bluetooth and Web Serial are currently only supported by + Chromium-based browsers. + + + Read more:  + + Web Bluetooth + +   + + Web Serial + + + + )} +
    +
    + ); +}; diff --git a/src/components/Dialog/QRDialog.tsx b/src/components/Dialog/QRDialog.tsx index 2648fb7f..88c8fc09 100644 --- a/src/components/Dialog/QRDialog.tsx +++ b/src/components/Dialog/QRDialog.tsx @@ -1,23 +1,29 @@ import { useEffect, useState } from "react"; import { fromByteArray } from "base64-js"; -import { toast } from "react-hot-toast"; import { QRCode } from "react-qrcode-logo"; import { Checkbox } from "@components/form/Checkbox.js"; import { Input } from "@components/form/Input.js"; -import { Dialog } from "@components/generic/Dialog.js"; -import { ClipboardIcon } from "@heroicons/react/24/outline"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle +} from "@components/UI/Dialog.js"; +import { ClipboardIcon } from "lucide-react"; import { Protobuf } from "@meshtastic/meshtasticjs"; export interface QRDialogProps { - isOpen: boolean; - close: () => void; + open: boolean; + onOpenChange: (open: boolean) => void; loraConfig?: Protobuf.Config_LoRaConfig; channels: Protobuf.Channel[]; } export const QRDialog = ({ - isOpen, - close, + open, + onOpenChange, loraConfig, channels }: QRDialogProps): JSX.Element => { @@ -44,58 +50,57 @@ export const QRDialog = ({ }, [channels, selectedChannels, loraConfig]); return ( - -
    -
    - {channels.map((channel) => ( - { - if (selectedChannels.includes(channel.index)) { - setSelectedChannels( - selectedChannels.filter((c) => c !== channel.index) - ); - } else { - setSelectedChannels([...selectedChannels, channel.index]); - } - }} - /> - ))} + + + + Generate QR Code + + The current LoRa configuration will also be shared. + + +
    +
    +
    + {channels.map((channel) => ( + { + if (selectedChannels.includes(channel.index)) { + setSelectedChannels( + selectedChannels.filter((c) => c !== channel.index) + ); + } else { + setSelectedChannels([...selectedChannels, channel.index]); + } + }} + /> + ))} +
    + +
    - -
    - -
    - , - action() { - void navigator.clipboard.writeText(QRCodeURL); - toast.success("Copied URL to Clipboard"); - } - }} - /> -
    + + , + action() { + void navigator.clipboard.writeText(QRCodeURL); + } + }} + /> + +
    ); }; diff --git a/src/components/Dialog/RebootDialog.tsx b/src/components/Dialog/RebootDialog.tsx index 6dd07766..e425da20 100644 --- a/src/components/Dialog/RebootDialog.tsx +++ b/src/components/Dialog/RebootDialog.tsx @@ -1,56 +1,64 @@ import { useState } from "react"; -import { useDevice } from "@core/providers/useDevice.js"; -import { Dialog } from "@components/generic/Dialog.js"; -import { ArrowPathIcon, ClockIcon } from "@heroicons/react/24/outline"; -import { Button } from "@components/form/Button.js"; +import { useDevice } from "@core/stores/deviceStore.js"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle +} from "@components/UI/Dialog.js"; +import { ClockIcon, RefreshCwIcon } from "lucide-react"; +import { Button } from "@components/UI/Button.js"; import { Input } from "@components/form/Input.js"; export interface RebootDialogProps { - isOpen: boolean; - close: () => void; + open: boolean; + onOpenChange: (open: boolean) => void; } export const RebootDialog = ({ - isOpen, - close + open, + onOpenChange }: RebootDialogProps): JSX.Element => { const { connection, setDialogOpen } = useDevice(); const [time, setTime] = useState(5); return ( - -
    - setTime(parseInt(e.target.value))} - action={{ - icon: , - action() { - connection - ?.reboot(time * 60) - .then(() => setDialogOpen("reboot", false)); - } - }} - /> - -
    + + + + Schedule Reboot + + Reboot the connected node after x minutes. + + +
    + setTime(parseInt(e.target.value))} + action={{ + icon: , + action() { + connection + ?.reboot(time * 60) + .then(() => setDialogOpen("reboot", false)); + } + }} + /> + +
    +
    ); }; diff --git a/src/components/Dialog/ShutdownDialog.tsx b/src/components/Dialog/ShutdownDialog.tsx index cf628a63..2e635d10 100644 --- a/src/components/Dialog/ShutdownDialog.tsx +++ b/src/components/Dialog/ShutdownDialog.tsx @@ -1,56 +1,66 @@ import { useState } from "react"; -import { useDevice } from "@core/providers/useDevice.js"; -import { Dialog } from "@components/generic/Dialog.js"; -import { ClockIcon, PowerIcon } from "@heroicons/react/24/outline"; -import { Button } from "@components/form/Button.js"; +import { useDevice } from "@core/stores/deviceStore.js"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle +} from "@components/UI/Dialog.js"; +import { ClockIcon, PowerIcon } from "lucide-react"; +import { Button } from "@components/UI/Button.js"; import { Input } from "@components/form/Input.js"; export interface ShutdownDialogProps { - isOpen: boolean; - close: () => void; + open: boolean; + onOpenChange: (open: boolean) => void; } export const ShutdownDialog = ({ - isOpen, - close + open, + onOpenChange }: ShutdownDialogProps): JSX.Element => { const { connection, setDialogOpen } = useDevice(); const [time, setTime] = useState(5); return ( - -
    - setTime(parseInt(e.target.value))} - action={{ - icon: , - action() { + + + + Schedule Shutdown + + Turn off the connected node after x minutes. + + + +
    + setTime(parseInt(e.target.value))} + action={{ + icon: , + action() { + connection + ?.shutdown(time * 60) + .then(() => setDialogOpen("shutdown", false)); + } + }} + /> + -
    + }} + > + + Now + +
    +
    ); }; diff --git a/src/components/Drawer/Metrics.tsx b/src/components/Drawer/Metrics.tsx deleted file mode 100644 index 6399de81..00000000 --- a/src/components/Drawer/Metrics.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import "chartjs-adapter-date-fns"; -import { - Chart as ChartJS, - Filler, - Legend, - LinearScale, - LineElement, - PointElement, - TimeSeriesScale, - Tooltip -} from "chart.js"; -import { Line } from "react-chartjs-2"; -import { useDevice } from "@core/providers/useDevice.js"; - -export const Metrics = (): JSX.Element => { - const { nodes, hardware } = useDevice(); - - const myNode = nodes.find((n) => n.data.num === hardware.myNodeNum); - - ChartJS.register( - LinearScale, - PointElement, - LineElement, - Tooltip, - Filler, - Legend, - TimeSeriesScale - ); - - return ( -
    - { - return { - x: metric.timestamp, - y: metric.metric.airUtilTx - }; - }), - backgroundColor: "rgba(102, 126, 234, 0.25)", - borderColor: "rgba(102, 126, 234, 1)", - pointBackgroundColor: "rgba(102, 126, 234, 1)" - }, - { - fill: true, - label: "channelUtilization", - yAxisID: "y1", - data: myNode?.deviceMetrics.map((metric) => { - return { - x: metric.timestamp, - y: metric.metric.channelUtilization - }; - }), - backgroundColor: "rgba(237, 100, 166, 0.25)", - borderColor: "rgba(237, 100, 166, 1)", - pointBackgroundColor: "rgba(237, 100, 166, 1)" - }, - { - fill: true, - label: "batteryLevel", - yAxisID: "y2", - data: myNode?.deviceMetrics.map((metric) => { - return { - x: metric.timestamp, - y: metric.metric.batteryLevel - }; - }), - backgroundColor: "rgba(113, 234, 102, 0.25)", - borderColor: "rgba(113, 234, 102, 1)", - pointBackgroundColor: "rgba(113, 234, 102, 1)" - }, - { - fill: true, - label: "voltage", - yAxisID: "y3", - data: myNode?.deviceMetrics.map((metric) => { - return { - x: metric.timestamp, - y: metric.metric.voltage - }; - }), - backgroundColor: "rgba(234, 166, 102, 0.25)", - borderColor: "rgba(234, 166, 102, 1)", - pointBackgroundColor: "rgba(234, 166, 102, 1)" - } - ] - }} - /> -
    - ); -}; diff --git a/src/components/Drawer/Notifications.tsx b/src/components/Drawer/Notifications.tsx deleted file mode 100644 index ad994937..00000000 --- a/src/components/Drawer/Notifications.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export const Notifications = (): JSX.Element => { - return
    ; -}; diff --git a/src/components/Drawer/Sensor.tsx b/src/components/Drawer/Sensor.tsx deleted file mode 100644 index fbbfe16d..00000000 --- a/src/components/Drawer/Sensor.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import "chartjs-adapter-date-fns"; - -import { - Chart as ChartJS, - Filler, - Legend, - LinearScale, - LineElement, - PointElement, - TimeSeriesScale, - Tooltip -} from "chart.js"; -import { Line } from "react-chartjs-2"; - -import { useDevice } from "@core/providers/useDevice.js"; - -export const Sensor = (): JSX.Element => { - const { nodes, hardware } = useDevice(); - - const myNode = nodes.find((n) => n.data.num === hardware.myNodeNum); - - ChartJS.register( - LinearScale, - PointElement, - LineElement, - Tooltip, - Filler, - Legend, - TimeSeriesScale - ); - - return ( -
    - { - return { - x: metric.timestamp, - y: metric.metric.barometricPressure - }; - }), - backgroundColor: "rgba(102, 126, 234, 0.25)", - borderColor: "rgba(102, 126, 234, 1)", - pointBackgroundColor: "rgba(102, 126, 234, 1)" - }, - { - fill: true, - label: "current", - yAxisID: "y1", - data: myNode?.environmentMetrics.map((metric) => { - return { - x: metric.timestamp, - y: metric.metric.current - }; - }), - backgroundColor: "rgba(237, 100, 166, 0.25)", - borderColor: "rgba(237, 100, 166, 1)", - pointBackgroundColor: "rgba(237, 100, 166, 1)" - }, - { - fill: true, - label: "gasResistance", - yAxisID: "y2", - data: myNode?.environmentMetrics.map((metric) => { - return { - x: metric.timestamp, - y: metric.metric.gasResistance - }; - }), - backgroundColor: "rgba(113, 234, 102, 0.25)", - borderColor: "rgba(113, 234, 102, 1)", - pointBackgroundColor: "rgba(113, 234, 102, 1)" - }, - { - fill: true, - label: "relativeHumidity", - yAxisID: "y3", - data: myNode?.environmentMetrics.map((metric) => { - return { - x: metric.timestamp, - y: metric.metric.relativeHumidity - }; - }), - backgroundColor: "rgba(234, 166, 102, 0.25)", - borderColor: "rgba(234, 166, 102, 1)", - pointBackgroundColor: "rgba(234, 166, 102, 1)" - }, - { - fill: true, - label: "temperature", - yAxisID: "y4", - data: myNode?.environmentMetrics.map((metric) => { - return { - x: metric.timestamp, - y: metric.metric.temperature - }; - }), - backgroundColor: "rgba(38, 255, 212, 0.25)", - borderColor: "rgba(38, 255, 212, 1)", - pointBackgroundColor: "rgba(38, 255, 212, 1)" - }, - { - fill: true, - label: "voltage", - yAxisID: "y5", - data: myNode?.environmentMetrics.map((metric) => { - return { - x: metric.timestamp, - y: metric.metric.voltage - }; - }), - backgroundColor: "rgba(247, 255, 15, 0.25)", - borderColor: "rgba(247, 255, 15, 1)", - pointBackgroundColor: "rgba(247, 255, 15, 1)" - } - ] - }} - /> -
    - ); -}; diff --git a/src/components/Drawer/index.tsx b/src/components/Drawer/index.tsx deleted file mode 100644 index 2fce27ee..00000000 --- a/src/components/Drawer/index.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { useState } from "react"; -import { Metrics } from "@components/Drawer/Metrics.js"; -import { Notifications } from "@components/Drawer/Notifications.js"; -import { Sensor } from "@components/Drawer/Sensor.js"; -import type { TabType } from "@components/generic/TabbedContent.js"; -import { Tab } from "@headlessui/react"; -import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/24/outline"; - -export const Drawer = (): JSX.Element => { - const [drawerOpen, setDrawerOpen] = useState(false); - - const tabs: TabType[] = [ - { label: "Notifications", element: Notifications }, - { label: "Metrics", element: Metrics }, - { label: "Sensor", element: Sensor } - ]; - return ( - - - {tabs.map((tab, index) => ( - - {({ selected }) => ( -
    { - setDrawerOpen(true); - }} - className={`flex h-full cursor-pointer border-b-4 px-1 first:pl-2 last:pr-2 hover:text-textPrimary ${ - selected - ? "border-accent text-textPrimary" - : "border-backgroundPrimary text-textSecondary" - }`} - > - {tab.label} -
    - )} -
    - ))} - -
    -
    { - setDrawerOpen(!drawerOpen); - }} - className="flex cursor-pointer px-2" - > -
    - {drawerOpen ? ( - - ) : ( - - )} -
    -
    -
    -
    - - - {tabs.map((tab, index) => ( - - {tab.element} - - ))} - -
    - ); -}; diff --git a/src/components/DynamicForm.tsx b/src/components/DynamicForm.tsx new file mode 100644 index 00000000..65033afc --- /dev/null +++ b/src/components/DynamicForm.tsx @@ -0,0 +1,238 @@ +import { + Controller, + DeepPartial, + FieldValues, + Path, + SubmitHandler, + useForm +} from "react-hook-form"; +import { Input } from "./UI/Input.js"; +import { Label } from "./UI/Label.js"; +import { ErrorMessage } from "@hookform/error-message"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from "./UI/Select.js"; +import { Switch } from "./UI/Switch.js"; +import { H4 } from "./UI/Typography/H4.js"; +import { Subtle } from "./UI/Typography/Subtle.js"; + +interface DisabledBy { + fieldName: Path; + selector?: number; + invert?: boolean; +} + +interface BasicFieldProps { + name: Path; + label: string; + description?: string; + active?: boolean; + required?: boolean; + disabledBy?: DisabledBy[]; +} + +interface InputFieldProps extends BasicFieldProps { + type: "text" | "number" | "password"; + suffix?: string; +} + +interface SelectFieldProps extends BasicFieldProps { + type: "select" | "multiSelect"; + + enumValue: { + [s: string]: string | number; + }; + formatEnumName?: boolean; +} + +interface ToggleFieldProps extends BasicFieldProps { + type: "toggle"; +} + +export interface FormProps { + onSubmit: SubmitHandler; + defaultValues?: DeepPartial; + fieldGroups: { + label: string; + description: string; + fields: (InputFieldProps | SelectFieldProps | ToggleFieldProps)[]; + }[]; +} + +export function DynamicForm({ + fieldGroups, + onSubmit, + defaultValues +}: FormProps) { + const { register, handleSubmit, control, getValues } = useForm({ + mode: "onChange", + defaultValues: defaultValues + }); + + const isDisabled = (disabledBy?: DisabledBy[]): boolean => { + if (!disabledBy) return false; + + return disabledBy.some((field) => { + const value = getValues(field.fieldName); + if (typeof value === "boolean") return field.invert ? value : !value; + if (typeof value === "number") + return field.invert + ? field.selector !== value + : field.selector === value; + return false; + }); + }; + + return ( +
    + {fieldGroups.map((fieldGroup) => ( +
    +
    +

    {fieldGroup.label}

    + {fieldGroup.description} +
    + + {fieldGroup.fields.map((field) => { + const fieldWrapperData: FieldWrapperProps = { + label: field.label, + description: field.description, + disabled: isDisabled(field.disabledBy) + }; + + switch (field.type) { + case "text": + return ( + + + + ); + case "number": + return ( + + + + ); + case "password": + return ( + + + + ); + case "toggle": + return ( + + ( + + )} + /> + + ); + case "select": + const optionsEnumValues = field.enumValue + ? Object.entries(field.enumValue).filter( + (value) => typeof value[1] === "number" + ) + : []; + return ( + + ( + + )} + /> + + ); + case "multiSelect": + return tmp; + } + })} +
    + ))} +
    + ); +} + +interface FieldWrapperProps { + label: string; + description?: string; + disabled?: boolean; + children?: React.ReactNode; +} + +const FieldWrapper = ({ + label, + description, + disabled, + children +}: FieldWrapperProps): JSX.Element => ( +
    +
    +
    + +
    +
    +

    {description}

    +
    +
    {children}
    +
    +
    +
    +
    +
    +
    +); diff --git a/src/components/NewDevice.tsx b/src/components/NewDevice.tsx deleted file mode 100644 index 2169fc99..00000000 --- a/src/components/NewDevice.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from "react"; - -import { TabbedContent, TabType } from "@components/generic/TabbedContent.js"; -import { BLE } from "@components/PageComponents/Connect/BLE.js"; -import { HTTP } from "@components/PageComponents/Connect/HTTP.js"; -import { Serial } from "@components/PageComponents/Connect/Serial.js"; -import { useAppStore } from "@core/stores/appStore.js"; -import { MoonIcon, SunIcon } from "@heroicons/react/24/outline"; - -export const NewDevice = () => { - const { darkMode, setDarkMode } = useAppStore(); - - const tabs: TabType[] = [ - { - label: "Bluetooth", - element: BLE, - disabled: !navigator.bluetooth, - disabledMessage: - "Web Bluetooth is currently only supported by Chromium-based browsers", - disabledLink: - "https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API#browser_compatibility" - }, - { - label: "HTTP", - element: HTTP, - disabled: false, - disabledMessage: "Unsuported connection method" - }, - { - label: "Serial", - element: Serial, - disabled: !navigator.serial, - disabledMessage: - "WebSerial is currently only supported by Chromium based browsers: https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API#browser_compatibility" - } - ]; - - return ( -
    - -
    - ); -}; diff --git a/src/components/PageComponents/AppConfig/Map.tsx b/src/components/PageComponents/AppConfig/Map.tsx deleted file mode 100644 index 516836f7..00000000 --- a/src/components/PageComponents/AppConfig/Map.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { Controller, useFieldArray, useForm } from "react-hook-form"; -import { Button } from "@components/form/Button.js"; -import { IconButton } from "@components/form/IconButton.js"; -import { InfoWrapper } from "@components/form/InfoWrapper.js"; -import { Input } from "@components/form/Input.js"; -import { Toggle } from "@components/form/Toggle.js"; -import { useAppStore } from "@core/stores/appStore.js"; -import { MapValidation } from "@app/validation/appConfig/map.js"; -import { Form } from "@components/form/Form"; -import { TrashIcon } from "@heroicons/react/24/outline"; -import { classValidatorResolver } from "@hookform/resolvers/class-validator"; - -export const Map = (): JSX.Element => { - const { rasterSources, setRasterSources } = useAppStore(); - - const { - register, - handleSubmit, - formState: { errors, isDirty }, - control, - reset - } = useForm({ - defaultValues: { - // wmsSources: wmsSources ?? [ - // { - // url: "", - // tileSize: 512, - // type: "raster" - // } - // ] - }, - resolver: classValidatorResolver(MapValidation) - }); - - const { fields, append, remove, insert } = useFieldArray({ - control, - name: "rasterSources" - }); - - const onSubmit = handleSubmit((data) => { - setRasterSources(data.rasterSources); - }); - - // useEffect(() => { - // reset(rasterSources); - // }, [reset, rasterSources]); - - return ( -
    - -
    - {fields.map((field, index) => ( -
    - ( - - )} - /> - - - - } - onClick={() => { - remove(index); - }} - /> -
    - ))} - -
    -
    -
    - ); -}; diff --git a/src/components/PageComponents/Channel.tsx b/src/components/PageComponents/Channel.tsx index b9bdfa8d..06f1b5d0 100644 --- a/src/components/PageComponents/Channel.tsx +++ b/src/components/PageComponents/Channel.tsx @@ -1,21 +1,13 @@ import { useEffect, useState } from "react"; import { fromByteArray, toByteArray } from "base64-js"; import { Controller, useForm } from "react-hook-form"; -import { toast } from "react-hot-toast"; import { ChannelSettingsValidation } from "@app/validation/channelSettings.js"; -import { Form } from "@components/form/Form"; import { Input } from "@components/form/Input.js"; -import { Select } from "@components/form/Select.js"; import { Toggle } from "@components/form/Toggle.js"; -import { useDevice } from "@core/providers/useDevice.js"; -import { - ArrowPathIcon, - EyeIcon, - EyeSlashIcon -} from "@heroicons/react/24/outline"; +import { useDevice } from "@core/stores/deviceStore.js"; +import { RefreshCwIcon, EyeIcon, EyeOffIcon } from "lucide-react"; import { classValidatorResolver } from "@hookform/resolvers/class-validator"; import { Protobuf } from "@meshtastic/meshtasticjs"; -import { NavBar } from "@app/Nav/NavBar.js"; export interface SettingsPanelProps { channel: Protobuf.Channel; @@ -61,62 +53,41 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { }, [channel, reset]); const onSubmit = handleSubmit((data) => { - if (connection) { - void toast.promise( - connection - .setChannel( - new Protobuf.Channel({ - role: - channel?.role === Protobuf.Channel_Role.PRIMARY - ? Protobuf.Channel_Role.PRIMARY - : data.enabled - ? Protobuf.Channel_Role.SECONDARY - : Protobuf.Channel_Role.DISABLED, - index: channel?.index, - settings: { - ...data, - psk: toByteArray(data.psk ?? "") - } - }) - ) - .then(() => - addChannel({ - config: new Protobuf.Channel({ - index: channel.index, - role: channel.role, - settings: { - ...data, - psk: toByteArray(data.psk ?? "") - } - }), - lastInterraction: new Date(), - messages: [] - }) - ), - { - loading: "Saving...", - success: "Saved Channel", - error: "No response received" - } + connection + ?.setChannel( + new Protobuf.Channel({ + role: + channel?.role === Protobuf.Channel_Role.PRIMARY + ? Protobuf.Channel_Role.PRIMARY + : data.enabled + ? Protobuf.Channel_Role.SECONDARY + : Protobuf.Channel_Role.DISABLED, + index: channel?.index, + settings: { + ...data, + psk: toByteArray(data.psk ?? "") + } + }) + ) + .then(() => + addChannel({ + config: new Protobuf.Channel({ + index: channel.index, + role: channel.role, + settings: { + ...data, + psk: toByteArray(data.psk ?? "") + } + }), + lastInterraction: new Date(), + messages: [] + }) ); - } }); return ( -
    - - -
    +
    + {channel?.index !== 0 && ( <> { /> )} - + */} - ) : ( - - ), + icon: pskHidden ? : , action: () => { setPskHidden(!pskHidden); } @@ -202,7 +169,7 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { /> )} /> - +
    ); }; diff --git a/src/components/PageComponents/Config/Bluetooth.tsx b/src/components/PageComponents/Config/Bluetooth.tsx index 39431788..736b2b43 100644 --- a/src/components/PageComponents/Config/Bluetooth.tsx +++ b/src/components/PageComponents/Config/Bluetooth.tsx @@ -1,30 +1,12 @@ -import { useEffect } from "react"; -import { Controller, useForm, useWatch } from "react-hook-form"; -import { Input } from "@components/form/Input.js"; -import { Select } from "@components/form/Select.js"; -import { Toggle } from "@components/form/Toggle.js"; -import { BluetoothValidation } from "@app/validation/config/bluetooth.js"; -import { Form } from "@components/form/Form"; -import { useDevice } from "@core/providers/useDevice.js"; -import { renderOptions } from "@core/utils/selectEnumOptions.js"; -import { classValidatorResolver } from "@hookform/resolvers/class-validator"; +import type { BluetoothValidation } from "@app/validation/config/bluetooth.js"; +import { useDevice } from "@core/stores/deviceStore.js"; import { Protobuf } from "@meshtastic/meshtasticjs"; +import { DynamicForm } from "@app/components/DynamicForm.js"; export const Bluetooth = (): JSX.Element => { const { config, setWorkingConfig } = useDevice(); - const { register, handleSubmit, control, reset } = - useForm({ - mode: "onChange", - defaultValues: config.bluetooth, - resolver: classValidatorResolver(BluetoothValidation) - }); - - useEffect(() => { - reset(config.bluetooth); - }, [reset, config.bluetooth]); - - const onSubmit = handleSubmit((data) => { + const onSubmit = (data: BluetoothValidation) => { setWorkingConfig( new Protobuf.Config({ payloadVariant: { @@ -33,47 +15,56 @@ export const Bluetooth = (): JSX.Element => { } }) ); - }); - - const pairingMode = useWatch({ - control, - name: "mode", - defaultValue: Protobuf.Config_BluetoothConfig_PairingMode.RANDOM_PIN - }); + }; return ( -
    - ( - - )} - /> - - - + onSubmit={onSubmit} + defaultValues={config.bluetooth} + fieldGroups={[ + { + label: "Bluetooth Settings", + description: "Settings for the Bluetooth module", + fields: [ + { + type: "toggle", + name: "enabled", + label: "Enabled", + description: "Enable or disable Bluetooth" + }, + { + type: "select", + name: "mode", + label: "Pairing mode", + description: "Pin selection behaviour.", + enumValue: Protobuf.Config_BluetoothConfig_PairingMode, + formatEnumName: true, + disabledBy: [ + { + fieldName: "enabled" + } + ] + }, + { + type: "number", + name: "fixedPin", + label: "Pin", + description: "Pin to use when pairing", + disabledBy: [ + { + fieldName: "mode", + selector: + Protobuf.Config_BluetoothConfig_PairingMode.FIXED_PIN, + invert: true + }, + { + fieldName: "enabled" + } + ] + } + ] } - label="Pin" - description="Pin to use when pairing" - type="number" - {...register("fixedPin", { - valueAsNumber: true - })} - /> - + ]} + /> ); }; diff --git a/src/components/PageComponents/Config/Device.tsx b/src/components/PageComponents/Config/Device.tsx index d59ae960..c93298b1 100644 --- a/src/components/PageComponents/Config/Device.tsx +++ b/src/components/PageComponents/Config/Device.tsx @@ -1,28 +1,12 @@ -import { useEffect } from "react"; -import { Controller, useForm } from "react-hook-form"; -import { Input } from "@components/form/Input.js"; -import { Select } from "@components/form/Select.js"; -import { Toggle } from "@components/form/Toggle.js"; -import { DeviceValidation } from "@app/validation/config/device.js"; -import { Form } from "@components/form/Form"; -import { useDevice } from "@core/providers/useDevice.js"; -import { renderOptions } from "@core/utils/selectEnumOptions.js"; -import { classValidatorResolver } from "@hookform/resolvers/class-validator"; +import type { DeviceValidation } from "@app/validation/config/device.js"; +import { useDevice } from "@core/stores/deviceStore.js"; import { Protobuf } from "@meshtastic/meshtasticjs"; +import { DynamicForm } from "@app/components/DynamicForm.js"; export const Device = (): JSX.Element => { const { config, setWorkingConfig } = useDevice(); - const { register, handleSubmit, control, reset } = useForm({ - mode: "onChange", - defaultValues: config.device, - resolver: classValidatorResolver(DeviceValidation) - }); - useEffect(() => { - reset(config.device); - }, [reset, config.device]); - - const onSubmit = handleSubmit((data) => { + const onSubmit = (data: DeviceValidation) => { setWorkingConfig( new Protobuf.Config({ payloadVariant: { @@ -31,53 +15,68 @@ export const Device = (): JSX.Element => { } }) ); - }); + }; return ( -
    - - ( - - )} - /> - ( - - )} - /> - - - + + onSubmit={onSubmit} + defaultValues={config.device} + fieldGroups={[ + { + label: "Device Settings", + description: "Settings for the device", + fields: [ + { + type: "select", + name: "role", + label: "Role", + description: "What role the device performs on the mesh", + enumValue: Protobuf.Config_DeviceConfig_Role, + formatEnumName: true + }, + { + type: "toggle", + name: "serialEnabled", + label: "Serial Output Enabled", + description: "Disable the device's serial console" + }, + { + type: "toggle", + name: "debugLogEnabled", + label: "Enabled Debug Log", + description: + "Output debugging information to the device's serial port (auto disables when serial client is connected)" + }, + { + type: "number", + name: "buttonGpio", + label: "Button Pin", + description: "Button pin override" + }, + { + type: "number", + name: "buzzerGpio", + label: "Buzzer Pin", + description: "Buzzer pin override" + }, + { + type: "select", + name: "rebroadcastMode", + label: "Rebroadcast Mode", + description: "How to handle rebroadcasting", + enumValue: Protobuf.Config_DeviceConfig_RebroadcastMode, + formatEnumName: true + }, + { + type: "number", + name: "nodeInfoBroadcastSecs", + label: "Node Info Broadcast Interval", + description: "How often to broadcast node info", + suffix: "Seconds" + } + ] + } + ]} + /> ); }; diff --git a/src/components/PageComponents/Config/Display.tsx b/src/components/PageComponents/Config/Display.tsx index f3681681..dd3fd423 100644 --- a/src/components/PageComponents/Config/Display.tsx +++ b/src/components/PageComponents/Config/Display.tsx @@ -1,30 +1,12 @@ -import { useEffect } from "react"; -import { Controller, useForm } from "react-hook-form"; -import { Input } from "@components/form/Input.js"; -import { Select } from "@components/form/Select.js"; -import { Toggle } from "@components/form/Toggle.js"; -import { DisplayValidation } from "@app/validation/config/display.js"; -import { Form } from "@components/form/Form"; -import { useDevice } from "@core/providers/useDevice.js"; -import { renderOptions } from "@core/utils/selectEnumOptions.js"; -import { classValidatorResolver } from "@hookform/resolvers/class-validator"; +import type { DisplayValidation } from "@app/validation/config/display.js"; +import { useDevice } from "@core/stores/deviceStore.js"; import { Protobuf } from "@meshtastic/meshtasticjs"; +import { DynamicForm } from "@app/components/DynamicForm.js"; export const Display = (): JSX.Element => { const { config, setWorkingConfig } = useDevice(); - const { register, handleSubmit, reset, control } = useForm( - { - mode: "onChange", - defaultValues: config.display, - resolver: classValidatorResolver(DisplayValidation) - } - ); - useEffect(() => { - reset(config.display); - }, [reset, config.display]); - - const onSubmit = handleSubmit((data) => { + const onSubmit = (data: DisplayValidation) => { setWorkingConfig( new Protobuf.Config({ payloadVariant: { @@ -33,88 +15,80 @@ export const Display = (): JSX.Element => { } }) ); - }); + }; return ( -
    - - - - ( - - )} - /> - ( - - )} - /> - - - - ( - - )} - /> - + + onSubmit={onSubmit} + defaultValues={config.display} + fieldGroups={[ + { + label: "Display Settings", + description: "Settings for the device display", + fields: [ + { + type: "number", + name: "screenOnSecs", + label: "Screen Timeout", + description: "Turn off the display after this long" + }, + { + type: "select", + name: "gpsFormat", + label: "GPS Display Units", + description: "Coordinate display format", + enumValue: Protobuf.Config_DisplayConfig_GpsCoordinateFormat + }, + { + type: "number", + name: "autoScreenCarouselSecs", + label: "Carousel Delay", + description: "How fast to cycle through windows" + }, + { + type: "toggle", + name: "compassNorthTop", + label: "Compass North Top", + description: "Fix north to the top of compass" + }, + { + type: "toggle", + name: "flipScreen", + label: "Flip Screen", + description: "Flip display 180 degrees" + }, + { + type: "select", + name: "units", + label: "Display Units", + description: "Display metric or imperial units", + enumValue: Protobuf.Config_DisplayConfig_DisplayUnits, + formatEnumName: true + }, + { + type: "select", + name: "oled", + label: "OLED Type", + description: "Type of OLED screen attached to the device", + enumValue: Protobuf.Config_DisplayConfig_OledType + }, + { + type: "select", + name: "displaymode", + label: "Display Mode", + description: "Screen layout variant", + enumValue: Protobuf.Config_DisplayConfig_DisplayMode, + formatEnumName: true + }, + { + type: "toggle", + name: "headingBold", + label: "Bold Heading", + description: "Bolden the heading text" + } + ] + } + ]} + /> ); }; diff --git a/src/components/PageComponents/Config/LoRa.tsx b/src/components/PageComponents/Config/LoRa.tsx index f944ddf6..c8662f51 100644 --- a/src/components/PageComponents/Config/LoRa.tsx +++ b/src/components/PageComponents/Config/LoRa.tsx @@ -1,36 +1,12 @@ -import { useEffect } from "react"; -import { Controller, useForm, useWatch } from "react-hook-form"; -import { FormSection } from "@components/form/FormSection.js"; -import { Input } from "@components/form/Input.js"; -import { Select } from "@components/form/Select.js"; -import { Toggle } from "@components/form/Toggle.js"; -import { LoRaValidation } from "@app/validation/config/lora.js"; -import { Form } from "@components/form/Form"; -import { useDevice } from "@core/providers/useDevice.js"; -import { renderOptions } from "@core/utils/selectEnumOptions.js"; -import { classValidatorResolver } from "@hookform/resolvers/class-validator"; +import type { LoRaValidation } from "@app/validation/config/lora.js"; +import { useDevice } from "@core/stores/deviceStore.js"; import { Protobuf } from "@meshtastic/meshtasticjs"; +import { DynamicForm } from "@app/components/DynamicForm.js"; export const LoRa = (): JSX.Element => { const { config, setWorkingConfig } = useDevice(); - const { register, handleSubmit, control, reset } = useForm({ - mode: "onChange", - defaultValues: config.lora, - resolver: classValidatorResolver(LoRaValidation) - }); - - const usePreset = useWatch({ - control, - name: "usePreset", - defaultValue: true - }); - - useEffect(() => { - reset(config.lora); - }, [reset, config.lora]); - - const onSubmit = handleSubmit((data) => { + const onSubmit = (data: LoRaValidation) => { setWorkingConfig( new Protobuf.Config({ payloadVariant: { @@ -39,121 +15,148 @@ export const LoRa = (): JSX.Element => { } }) ); - }); + }; return ( -
    - - ( - - )} - /> - {usePreset ? ( - - ) : ( - <> - - - - - )} - - - ( - - )} - /> - - - - - ( - - )} - /> - - -
    + + onSubmit={onSubmit} + defaultValues={config.lora} + fieldGroups={[ + { + label: "Mesh Settings", + description: "Settings for the LoRa mesh", + fields: [ + { + type: "select", + name: "region", + label: "Region", + description: "Sets the region for your node", + enumValue: Protobuf.Config_LoRaConfig_RegionCode + }, + { + type: "number", + name: "hopLimit", + label: "Hop Limit", + description: "Maximum number of hops" + }, + { + type: "number", + name: "channelNum", + label: "Channel Number", + description: "LoRa channel number" + } + ] + }, + { + label: "Waveform Settings", + description: "Settings for the LoRa waveform", + fields: [ + { + type: "toggle", + name: "usePreset", + label: "Use Preset", + description: "Use one of the predefined modem presets" + }, + { + type: "select", + name: "modemPreset", + label: "Modem Preset", + description: "Modem preset to use", + enumValue: Protobuf.Config_LoRaConfig_ModemPreset, + formatEnumName: true, + disabledBy: [ + { + fieldName: "usePreset" + } + ] + }, + { + type: "number", + name: "bandwidth", + label: "Bandwidth", + description: "Channel bandwidth in MHz", + suffix: "MHz", + disabledBy: [ + { + fieldName: "usePreset", + invert: true + } + ] + }, + { + type: "number", + name: "spreadFactor", + label: "Spreading Factor", + description: "Indicates the number of chirps per symbol", + suffix: "CPS", + disabledBy: [ + { + fieldName: "usePreset", + invert: true + } + ] + }, + { + type: "number", + name: "codingRate", + label: "Coding Rate", + description: "The denominator of the coding rate", + disabledBy: [ + { + fieldName: "usePreset", + invert: true + } + ] + } + ] + }, + { + label: "Radio Settings", + description: "Settings for the LoRa radio", + fields: [ + { + type: "toggle", + name: "txEnabled", + label: "Tramsmit Enabled", + description: "Enable/Disable transmit (TX) from the LoRa radio" + }, + { + type: "number", + name: "txPower", + label: "Transmit Power", + description: "Max transmit power", + suffix: "dBm" + }, + { + type: "toggle", + name: "overrideDutyCycle", + label: "Override Duty Cycle", + description: "Override Duty Cycle" + }, + { + type: "number", + name: "frequencyOffset", + label: "Frequency Offset", + description: + "Frequency offset to correct for crystal calibration errors", + suffix: "Hz" + }, + { + type: "toggle", + name: "sx126xRxBoostedGain", + label: "Boosted RX Gain", + description: "Boosted RX gain" + }, + { + type: "number", + name: "overrideFrequency", + label: "Override Frequency", + description: "Override frequency", + suffix: "Hz" + } + ] + } + ]} + /> ); }; diff --git a/src/components/PageComponents/Config/Network.tsx b/src/components/PageComponents/Config/Network.tsx index 68d28f0c..ec79154a 100644 --- a/src/components/PageComponents/Config/Network.tsx +++ b/src/components/PageComponents/Config/Network.tsx @@ -1,51 +1,12 @@ -import { useEffect } from "react"; -import { Controller, useForm, useWatch } from "react-hook-form"; -import { FormSection } from "@components/form/FormSection.js"; -import { Input } from "@components/form/Input.js"; -import { IPInput } from "@components/form/IPInput.js"; -import { Select } from "@components/form/Select.js"; -import { Toggle } from "@components/form/Toggle.js"; -import { renderOptions } from "@core/utils/selectEnumOptions.js"; -import { NetworkValidation } from "@app/validation/config/network.js"; -import { Form } from "@components/form/Form"; -import { useDevice } from "@core/providers/useDevice.js"; -import { ErrorMessage } from "@hookform/error-message"; -import { classValidatorResolver } from "@hookform/resolvers/class-validator"; +import type { NetworkValidation } from "@app/validation/config/network.js"; +import { useDevice } from "@core/stores/deviceStore.js"; import { Protobuf } from "@meshtastic/meshtasticjs"; +import { DynamicForm } from "@app/components/DynamicForm.js"; export const Network = (): JSX.Element => { const { config, setWorkingConfig } = useDevice(); - const { register, handleSubmit, control, reset } = useForm( - { - mode: "onChange", - defaultValues: config.network, - resolver: classValidatorResolver(NetworkValidation) - } - ); - const wifiEnabled = useWatch({ - control, - name: "wifiEnabled", - defaultValue: false - }); - - const ethEnabled = useWatch({ - control, - name: "ethEnabled", - defaultValue: false - }); - - const ethMode = useWatch({ - control, - name: "addressMode", - defaultValue: Protobuf.Config_NetworkConfig_AddressMode.DHCP - }); - - useEffect(() => { - reset(config.network); - }, [reset, config.network]); - - const onSubmit = handleSubmit((data) => { + const onSubmit = (data: NetworkValidation) => { setWorkingConfig( new Protobuf.Config({ payloadVariant: { @@ -54,97 +15,143 @@ export const Network = (): JSX.Element => { } }) ); - }); + }; return ( -
    - - ( - - )} - /> - - - - - ( - - )} - /> - - - - {ethMode === Protobuf.Config_NetworkConfig_AddressMode.STATIC && ( - <> - - - - - - )} - - - -
    + + onSubmit={onSubmit} + defaultValues={config.network} + fieldGroups={[ + { + label: "WiFi Config", + description: "WiFi radio configuration", + fields: [ + { + type: "toggle", + name: "wifiEnabled", + label: "Enabled", + description: "Enable or disable the WiFi radio" + }, + { + type: "text", + name: "wifiSsid", + label: "SSID", + description: "Network name", + disabledBy: [ + { + fieldName: "wifiEnabled" + } + ] + }, + { + type: "password", + name: "wifiPsk", + label: "PSK", + description: "Network password", + disabledBy: [ + { + fieldName: "wifiEnabled" + } + ] + } + ] + }, + { + label: "Ethernet Config", + description: "Ethernet port configuration", + fields: [ + { + type: "toggle", + name: "ethEnabled", + label: "Enabled", + description: "Enable or disable the Ethernet port" + } + ] + }, + { + label: "IP Config", + description: "IP configuration", + fields: [ + { + type: "select", + name: "addressMode", + label: "Address Mode", + description: "Address assignment selection", + enumValue: Protobuf.Config_NetworkConfig_AddressMode + }, + { + type: "text", + name: "ipv4Config.ip", + label: "IP", + description: "IP Address", + disabledBy: [ + { + fieldName: "addressMode", + selector: Protobuf.Config_NetworkConfig_AddressMode.DHCP + } + ] + }, + { + type: "text", + name: "ipv4Config.gateway", + label: "Gateway", + description: "Default Gateway", + disabledBy: [ + { + fieldName: "addressMode", + selector: Protobuf.Config_NetworkConfig_AddressMode.DHCP + } + ] + }, + { + type: "text", + name: "ipv4Config.subnet", + label: "Subnet", + description: "Subnet Mask", + disabledBy: [ + { + fieldName: "addressMode", + selector: Protobuf.Config_NetworkConfig_AddressMode.DHCP + } + ] + }, + { + type: "text", + name: "ipv4Config.dns", + label: "DNS", + description: "DNS Server", + disabledBy: [ + { + fieldName: "addressMode", + selector: Protobuf.Config_NetworkConfig_AddressMode.DHCP + } + ] + } + ] + }, + { + label: "NTP Config", + description: "NTP configuration", + fields: [ + { + type: "text", + name: "ntpServer", + label: "NTP Server" + } + ] + }, + { + label: "Rsyslog Config", + description: "Rsyslog configuration", + fields: [ + { + type: "text", + name: "rsyslogServer", + label: "Rsyslog Server" + } + ] + } + ]} + /> ); }; diff --git a/src/components/PageComponents/Config/Position.tsx b/src/components/PageComponents/Config/Position.tsx index e0be95fe..bee58109 100644 --- a/src/components/PageComponents/Config/Position.tsx +++ b/src/components/PageComponents/Config/Position.tsx @@ -1,230 +1,97 @@ -import { useEffect } from "react"; -import { Controller, useForm, useWatch } from "react-hook-form"; -import { BitwiseSelect } from "@components/form/BitwiseSelect.js"; -import { FormSection } from "@components/form/FormSection.js"; -import { Input } from "@components/form/Input.js"; -import { Toggle } from "@components/form/Toggle.js"; -import { PositionValidation } from "@app/validation/config/position.js"; -import { Form } from "@components/form/Form"; -import { useDevice } from "@core/providers/useDevice.js"; -import { classValidatorResolver } from "@hookform/resolvers/class-validator"; +import type { PositionValidation } from "@app/validation/config/position.js"; +import { useDevice } from "@core/stores/deviceStore.js"; import { Protobuf } from "@meshtastic/meshtasticjs"; +import { DynamicForm } from "@app/components/DynamicForm.js"; export const Position = (): JSX.Element => { const { config, nodes, hardware, setWorkingConfig } = useDevice(); - const myNode = nodes.find((n) => n.data.num === hardware.myNodeNum); - - const { register, handleSubmit, reset, control } = - useForm({ - mode: "onChange", - defaultValues: { - fixedAlt: myNode?.data.position?.altitude, - fixedLat: (myNode?.data.position?.latitudeI ?? 0) / 1e7, - fixedLng: (myNode?.data.position?.longitudeI ?? 0) / 1e7, - ...config.position - }, - resolver: classValidatorResolver(PositionValidation) - }); - - const fixedPositionEnabled = useWatch({ - control, - name: "fixedPosition", - defaultValue: false - }); - - useEffect(() => { - reset({ - fixedAlt: myNode?.data.position?.altitude, - fixedLat: (myNode?.data.position?.latitudeI ?? 0) / 1e7, - fixedLng: (myNode?.data.position?.longitudeI ?? 0) / 1e7, - ...config.position - }); - }, [reset, config.position, myNode?.data.position]); - - const onSubmit = handleSubmit((data) => { - const { fixedAlt, fixedLat, fixedLng, ...rest } = data; - - const configHasChanged = !Protobuf.Config_PositionConfig.equals( - config.position, - new Protobuf.Config_PositionConfig(rest) - ); - + const onSubmit = (data: PositionValidation) => { setWorkingConfig( new Protobuf.Config({ payloadVariant: { case: "position", - value: rest + value: data } }) ); - - // if (connection) { - // void toast.promise( - // connection - // .setPosition( - // new Protobuf.Position({ - // altitude: fixedAlt, - // latitudeI: fixedLat * 1e7, - // longitudeI: fixedLng * 1e7 - // }) - // ) - // .then(() => reset({ ...data })), - // { - // loading: "Saving...", - // success: "Saved Position Config, Restarting Node", - // error: "No response received" - // } - // ); - // if (configHasChanged) { - // void toast.promise( - // connection - // .setConfig( - // new Protobuf.Config({ - // payloadVariant: { - // case: "position", - // value: rest - // } - // }) - // ) - // .then(() => - // setConfig( - // new Protobuf.Config({ - // payloadVariant: { - // case: "position", - // value: rest - // } - // }) - // ) - // ), - // { - // loading: "Saving...", - // success: "Saved Position Config, Restarting Node", - // error: "No response received" - // } - // ); - // } - // } - }); + }; return ( -
    - ( - - )} - /> - ( - - )} - /> - { - const { value, onChange } = field; - const { error } = fieldState; - - return ( - - ); - }} - /> - - ( - - )} - /> - {fixedPositionEnabled && ( - <> - - - - - )} - - - - - - - - - + + onSubmit={onSubmit} + defaultValues={config.position} + fieldGroups={[ + { + label: "Position settings", + description: "Settings for the position module", + fields: [ + { + type: "toggle", + name: "positionBroadcastSmartEnabled", + label: "Enable Smart Position", + description: + "Only send position when there has been a meaningful change in location" + }, + { + type: "toggle", + name: "fixedPosition", + label: "Fixed Position", + description: + "Don't report GPS position, but a manually-specified one" + }, + { + type: "toggle", + name: "gpsEnabled", + label: "GPS Enabled", + description: "Enable the internal GPS module" + }, + { + type: "multiSelect", + name: "positionFlags", + label: "Position Flags", + description: "Configuration options for Position messages", + enumValue: Protobuf.Config_PositionConfig_PositionFlags + }, + { + type: "number", + name: "rxGpio", + label: "Receive Pin", + description: "GPS Module RX pin override" + }, + { + type: "number", + name: "txGpio", + label: "Transmit Pin", + description: "GPS Module TX pin override" + } + ] + }, + { + label: "Intervals", + description: "How often to send position updates", + fields: [ + { + type: "number", + name: "positionBroadcastSecs", + label: "Broadcast Interval", + description: "How often your position is sent out over the mesh" + }, + { + type: "number", + name: "gpsUpdateInterval", + label: "GPS Update Interval", + description: "How often a GPS fix should be acquired" + }, + { + type: "number", + name: "gpsAttemptTime", + label: "Fix Attempt Duration", + description: "How long the device will try to get a fix for" + } + ] + } + ]} + /> ); }; diff --git a/src/components/PageComponents/Config/Power.tsx b/src/components/PageComponents/Config/Power.tsx index f8be2773..a645d0ac 100644 --- a/src/components/PageComponents/Config/Power.tsx +++ b/src/components/PageComponents/Config/Power.tsx @@ -1,27 +1,12 @@ -import { useEffect } from "react"; -import { Controller, useForm } from "react-hook-form"; -import { FormSection } from "@components/form/FormSection.js"; -import { Input } from "@components/form/Input.js"; -import { Toggle } from "@components/form/Toggle.js"; -import { PowerValidation } from "@app/validation/config/power.js"; -import { Form } from "@components/form/Form"; -import { useDevice } from "@core/providers/useDevice.js"; -import { classValidatorResolver } from "@hookform/resolvers/class-validator"; +import type { PowerValidation } from "@app/validation/config/power.js"; +import { useDevice } from "@core/stores/deviceStore.js"; import { Protobuf } from "@meshtastic/meshtasticjs"; +import { DynamicForm } from "@app/components/DynamicForm.js"; export const Power = (): JSX.Element => { const { config, setWorkingConfig } = useDevice(); - const { register, handleSubmit, reset, control } = useForm({ - mode: "onChange", - defaultValues: config.power, - resolver: classValidatorResolver(PowerValidation) - }); - useEffect(() => { - reset(config.power); - }, [reset, config.power]); - - const onSubmit = handleSubmit((data) => { + const onSubmit = (data: PowerValidation) => { setWorkingConfig( new Protobuf.Config({ payloadVariant: { @@ -30,72 +15,86 @@ export const Power = (): JSX.Element => { } }) ); - }); + }; return ( -
    - - ( - - )} - /> - - - - - - - - - + + onSubmit={onSubmit} + defaultValues={config.power} + fieldGroups={[ + { + label: "Power Config", + description: "Settings for the power module", + fields: [ + { + type: "toggle", + name: "isPowerSaving", + label: "Enable power saving mode", + description: + "Select if powered from a low-current source (i.e. solar), to minimize power consumption as much as possible." + }, + { + type: "number", + name: "onBatteryShutdownAfterSecs", + label: "Shutdown on battery delay", + description: + "Automatically shutdown node after this long when on battery, 0 for indefinite", + suffix: "Seconds" + }, + { + type: "number", + name: "adcMultiplierOverride", + label: "ADC Multiplier Override ratio", + description: "Used for tweaking battery voltage reading" + }, + { + type: "number", + name: "waitBluetoothSecs", + label: "No Connection Bluetooth Disabled", + description: + "If the device does not receive a Bluetooth connection, the BLE radio will be disabled after this long", + suffix: "Seconds" + } + ] + }, + { + label: "Sleep Settings", + description: "Sleep settings for the power module", + fields: [ + { + type: "number", + name: "meshSdsTimeoutSecs", + label: "Mesh SDS Timeout", + description: + "The device will enter super deep sleep after this time", + suffix: "Seconds" + }, + { + type: "number", + name: "sdsSecs", + label: "Super Deep Sleep Duration", + description: + "How long the device will be in super deep sleep for", + suffix: "Seconds" + }, + { + type: "number", + name: "lsSecs", + label: "Light Sleep Duration", + description: "How long the device will be in light sleep for", + suffix: "Seconds" + }, + { + type: "number", + name: "minWakeSecs", + label: "Minimum Wake Time", + description: + "Minimum amount of time the device will stay awake for after receiving a packet", + suffix: "Seconds" + } + ] + } + ]} + /> ); }; diff --git a/src/components/PageComponents/Config/User.tsx b/src/components/PageComponents/Config/User.tsx deleted file mode 100644 index df180c2b..00000000 --- a/src/components/PageComponents/Config/User.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import { useEffect } from "react"; -import { Controller, useForm } from "react-hook-form"; -import { toast } from "react-hot-toast"; -import { base16 } from "rfc4648"; -import { Input } from "@components/form/Input.js"; -import { Select } from "@components/form/Select.js"; -import { Toggle } from "@components/form/Toggle.js"; -import { UserValidation } from "@app/validation/config/user.js"; -import { Form } from "@components/form/Form"; -import { useDevice } from "@core/providers/useDevice.js"; -import { renderOptions } from "@core/utils/selectEnumOptions.js"; -import { classValidatorResolver } from "@hookform/resolvers/class-validator"; -import { Protobuf } from "@meshtastic/meshtasticjs"; - -export const User = (): JSX.Element => { - const { hardware, nodes, connection } = useDevice(); - - const myNode = nodes.find((n) => n.data.num === hardware.myNodeNum); - - const { register, handleSubmit, reset, control } = useForm({ - defaultValues: myNode?.data.user, - resolver: classValidatorResolver(UserValidation) - }); - - useEffect(() => { - reset({ - longName: myNode?.data.user?.longName, - shortName: myNode?.data.user?.shortName, - isLicensed: myNode?.data.user?.isLicensed - }); - }, [reset, myNode]); - - const onSubmit = handleSubmit((data) => { - if (connection && myNode?.data.user) { - void toast.promise( - connection - .setOwner( - new Protobuf.User({ - ...myNode.data.user, - ...data - }) - ) - .then(() => reset({ ...data })), - { - loading: "Saving...", - success: "Saved User, Restarting Node", - error: "No response received" - } - ); - } - }); - - return ( -
    - - - ( - - )} - /> - - - - - ); -}; diff --git a/src/components/PageComponents/Connect/BLE.tsx b/src/components/PageComponents/Connect/BLE.tsx index 290b0d3b..0e5cf350 100644 --- a/src/components/PageComponents/Connect/BLE.tsx +++ b/src/components/PageComponents/Connect/BLE.tsx @@ -1,11 +1,10 @@ import { useCallback, useEffect, useState } from "react"; import { Mono } from "@components/generic/Mono.js"; -import { Button } from "@components/form/Button.js"; +import { Button } from "@components/UI/Button.js"; import { useAppStore } from "@core/stores/appStore.js"; import { useDeviceStore } from "@core/stores/deviceStore.js"; import { subscribeAll } from "@core/subscriptions.js"; import { randId } from "@core/utils/randId.js"; -import { PlusCircleIcon } from "@heroicons/react/24/outline"; import { Constants, IBLEConnection } from "@meshtastic/meshtasticjs"; export const BLE = (): JSX.Element => { @@ -64,7 +63,6 @@ export const BLE = (): JSX.Element => { }); }} > - New device
    diff --git a/src/components/PageComponents/Connect/HTTP.tsx b/src/components/PageComponents/Connect/HTTP.tsx index 0c7f9cd9..f5e4e2d1 100644 --- a/src/components/PageComponents/Connect/HTTP.tsx +++ b/src/components/PageComponents/Connect/HTTP.tsx @@ -1,12 +1,11 @@ import { Controller, useForm, useWatch } from "react-hook-form"; import { Input } from "@components/form/Input.js"; import { Toggle } from "@components/form/Toggle.js"; -import { Button } from "@components/form/Button.js"; +import { Button } from "@components/UI/Button.js"; import { useAppStore } from "@core/stores/appStore.js"; import { useDeviceStore } from "@core/stores/deviceStore.js"; import { subscribeAll } from "@core/subscriptions.js"; import { randId } from "@core/utils/randId.js"; -import { PlusCircleIcon } from "@heroicons/react/24/outline"; import { IHTTPConnection } from "@meshtastic/meshtasticjs"; export const HTTP = (): JSX.Element => { @@ -71,8 +70,7 @@ export const HTTP = (): JSX.Element => { )} />
    - diff --git a/src/components/PageComponents/Connect/Serial.tsx b/src/components/PageComponents/Connect/Serial.tsx index efe25dc3..bcfc8d7a 100644 --- a/src/components/PageComponents/Connect/Serial.tsx +++ b/src/components/PageComponents/Connect/Serial.tsx @@ -1,11 +1,10 @@ import { useCallback, useEffect, useState } from "react"; import { Mono } from "@components/generic/Mono.js"; -import { Button } from "@components/form/Button.js"; +import { Button } from "@components/UI/Button.js"; import { useAppStore } from "@core/stores/appStore.js"; import { useDeviceStore } from "@core/stores/deviceStore.js"; import { subscribeAll } from "@core/subscriptions.js"; import { randId } from "@core/utils/randId.js"; -import { PlusCircleIcon } from "@heroicons/react/24/outline"; import { ISerialConnection } from "@meshtastic/meshtasticjs"; export const Serial = (): JSX.Element => { @@ -70,7 +69,6 @@ export const Serial = (): JSX.Element => { }); }} > - New device
    diff --git a/src/components/PageComponents/Map/MapControlls.tsx b/src/components/PageComponents/Map/MapControlls.tsx deleted file mode 100644 index a50c6b45..00000000 --- a/src/components/PageComponents/Map/MapControlls.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { useEffect } from "react"; - -import { useMap } from "react-map-gl"; - -import { useDevice } from "@core/providers/useDevice.js"; -import { - MagnifyingGlassMinusIcon, - MagnifyingGlassPlusIcon, - ShareIcon -} from "@heroicons/react/24/outline"; -import { bbox, lineString } from "@turf/turf"; - -export const MapControlls = (): JSX.Element => { - const { current: map } = useMap(); - const { nodes } = useDevice(); - - const getBBox = () => { - const nodesWithPosition = nodes.filter((n) => n.data.position?.latitudeI); - if (!nodesWithPosition.length) return; - const line = lineString( - nodesWithPosition.map((n) => [ - (n.data.position?.latitudeI ?? 0) / 1e7, - (n.data.position?.longitudeI ?? 0) / 1e7 - ]) - ); - const bounds = bbox(line); - const center = map?.cameraForBounds( - [ - [bounds[1], bounds[0]], - [bounds[3], bounds[2]] - ], - { padding: { top: 10, bottom: 10, left: 10, right: 10 } } - ); - if (center) map?.easeTo(center); - else if (nodesWithPosition.length === 1) - map?.easeTo({ - zoom: 12, - center: [ - (nodesWithPosition[0].data.position?.longitudeI ?? 0) / 1e7, - (nodesWithPosition[0].data.position?.latitudeI ?? 0) / 1e7 - ] - }); - }; - - useEffect(() => { - getBBox(); - }, []); - - return ( -
    -
    -
    { - map?.zoomIn(); - }} - > - -
    -
    { - map?.zoomOut(); - }} - > - -
    -
    getBBox()} - > - -
    -
    -
    - ); -}; diff --git a/src/components/PageComponents/Messages/ChannelChat.tsx b/src/components/PageComponents/Messages/ChannelChat.tsx index e195d19f..5c29e218 100644 --- a/src/components/PageComponents/Messages/ChannelChat.tsx +++ b/src/components/PageComponents/Messages/ChannelChat.tsx @@ -1,6 +1,6 @@ import { Message } from "@components/PageComponents/Messages/Message.js"; import { MessageInput } from "@components/PageComponents/Messages/MessageInput.js"; -import { useDevice } from "@core/providers/useDevice.js"; +import { useDevice } from "@core/stores/deviceStore.js"; import type { Channel } from "@core/stores/deviceStore.js"; export interface ChannelChatProps { diff --git a/src/components/PageComponents/Messages/Message.tsx b/src/components/PageComponents/Messages/Message.tsx index c97b06e1..c4db605d 100644 --- a/src/components/PageComponents/Messages/Message.tsx +++ b/src/components/PageComponents/Messages/Message.tsx @@ -1,12 +1,12 @@ import { WaypointMessage } from "@components/PageComponents/Messages/WaypointMessage.js"; -import { useDevice } from "@core/providers/useDevice.js"; +import { useDevice } from "@core/stores/deviceStore.js"; import type { AllMessageTypes } from "@core/stores/deviceStore.js"; import { Hashicon } from "@emeraldpay/hashicon-react"; import { - CheckCircleIcon, - EllipsisHorizontalCircleIcon, - ExclamationCircleIcon -} from "@heroicons/react/24/outline"; + CircleEllipsisIcon, + AlertCircleIcon, + CheckCircle2Icon +} from "lucide-react"; import type { Protobuf } from "@meshtastic/meshtasticjs"; export interface MessageProps { @@ -30,11 +30,11 @@ export const Message = ({ return lastMsgSameUser ? (
    {message.state === "ack" ? ( - + ) : message.state === "waiting" ? ( - + ) : ( - + )} {"waypointID" in message ? ( @@ -69,11 +69,14 @@ export const Message = ({
    {message.state === "ack" ? ( - + ) : message.state === "waiting" ? ( - + ) : ( - + )} {"waypointID" in message ? ( diff --git a/src/components/PageComponents/Messages/MessageInput.tsx b/src/components/PageComponents/Messages/MessageInput.tsx index f062140b..91a84ba1 100644 --- a/src/components/PageComponents/Messages/MessageInput.tsx +++ b/src/components/PageComponents/Messages/MessageInput.tsx @@ -1,9 +1,9 @@ -import { IconButton } from "@components/form/IconButton.js"; -import { Input } from "@components/form/Input.js"; -import { useDevice } from "@core/providers/useDevice.js"; +import { Input } from "@components/UI/Input.js"; +import { useDevice } from "@core/stores/deviceStore.js"; import type { Channel } from "@core/stores/deviceStore.js"; -import { MapPinIcon, PaperAirplaneIcon } from "@heroicons/react/24/outline"; +import { SendIcon } from "lucide-react"; import type { Types } from "@meshtastic/meshtasticjs"; +import { Button } from "@components/UI/Button.js"; export interface MessageInputProps { channel: Channel; @@ -42,18 +42,16 @@ export const MessageInput = ({ channel }: MessageInputProps): JSX.Element => { setMessageDraft(e.target.value)} /> - } - /> +
    - } />
    ); }; diff --git a/src/components/PageComponents/Messages/NewLocationMessage.tsx b/src/components/PageComponents/Messages/NewLocationMessage.tsx index 71d1cdbd..2d5e2ceb 100644 --- a/src/components/PageComponents/Messages/NewLocationMessage.tsx +++ b/src/components/PageComponents/Messages/NewLocationMessage.tsx @@ -1,8 +1,6 @@ import { Input } from "@components/form/Input.js"; -import { Select } from "@components/form/Select.js"; -import { Button } from "@components/form/Button.js"; -import { useDevice } from "@core/providers/useDevice.js"; -import { renderOptions } from "@core/utils/selectEnumOptions.js"; +import { Button } from "@components/UI/Button.js"; +import { useDevice } from "@core/stores/deviceStore.js"; import { Protobuf } from "@meshtastic/meshtasticjs"; enum LocationType { @@ -23,9 +21,9 @@ export const NewLocationMessage = (): JSX.Element => { > - {renderOptions(LocationType)} - + */}
    + + + {pages.map((link) => ( + { + setActivePage(link.page); + }} + active={link.page === activePage} + /> + ))} + + {children}
    ); }; diff --git a/src/components/Topbar.tsx b/src/components/Topbar.tsx new file mode 100644 index 00000000..a39291b8 --- /dev/null +++ b/src/components/Topbar.tsx @@ -0,0 +1,42 @@ +import { LucideIcon, AlignLeftIcon } from "lucide-react"; + +export interface PageLayoutProps { + label: string; + children: React.ReactNode; + actions?: { + icon: LucideIcon; + onClick: () => void; + }[]; +} + +export const PageLayout = ({ + label: title, + actions, + children +}: PageLayoutProps): JSX.Element => { + return ( +
    +
    + +
    +
    + {title} +
    + {actions?.map((action) => ( + + ))} +
    +
    +
    +
    + {children} +
    + ); +}; diff --git a/src/components/UI/Button.tsx b/src/components/UI/Button.tsx new file mode 100644 index 00000000..9a035fed --- /dev/null +++ b/src/components/UI/Button.tsx @@ -0,0 +1,53 @@ +import * as React from "react"; +import { VariantProps, cva } from "class-variance-authority"; + +import { cn } from "@core/utils/cn.js"; + +const buttonVariants = cva( + "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 dark:hover:bg-slate-800 dark:hover:text-slate-100 disabled:opacity-50 dark:focus:ring-slate-400 disabled:pointer-events-none dark:focus:ring-offset-slate-900 data-[state=open]:bg-slate-100 dark:data-[state=open]:bg-slate-800", + { + variants: { + variant: { + default: + "bg-slate-900 text-white hover:bg-slate-700 dark:bg-slate-50 dark:text-slate-900", + destructive: + "bg-red-500 text-white hover:bg-red-600 dark:hover:bg-red-600", + outline: + "bg-transparent border border-slate-200 hover:bg-slate-100 dark:border-slate-700 dark:text-slate-100", + subtle: + "bg-slate-100 text-slate-900 hover:bg-slate-200 dark:bg-slate-700 dark:text-slate-100", + ghost: + "bg-transparent hover:bg-slate-100 dark:hover:bg-slate-800 dark:text-slate-100 dark:hover:text-slate-100 data-[state=open]:bg-transparent dark:data-[state=open]:bg-transparent", + link: "bg-transparent underline-offset-4 hover:underline text-slate-900 dark:text-slate-100 hover:bg-transparent dark:hover:bg-transparent" + }, + size: { + default: "h-10 py-2 px-4", + sm: "h-9 px-2 rounded-md", + lg: "h-11 px-8 rounded-md" + } + }, + defaultVariants: { + variant: "default", + size: "default" + } + } +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps {} + +const Button = React.forwardRef( + ({ className, variant, size, ...props }, ref) => { + return ( + +); diff --git a/src/components/UI/Switch.tsx b/src/components/UI/Switch.tsx new file mode 100644 index 00000000..ed5167d2 --- /dev/null +++ b/src/components/UI/Switch.tsx @@ -0,0 +1,27 @@ +import * as React from "react"; +import * as SwitchPrimitives from "@radix-ui/react-switch"; + +import { cn } from "@core/utils/cn.js"; + +const Switch = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +Switch.displayName = SwitchPrimitives.Root.displayName; + +export { Switch }; diff --git a/src/components/UI/Tabs.tsx b/src/components/UI/Tabs.tsx new file mode 100644 index 00000000..9497fa1a --- /dev/null +++ b/src/components/UI/Tabs.tsx @@ -0,0 +1,53 @@ +import * as React from "react"; +import * as TabsPrimitive from "@radix-ui/react-tabs"; + +import { cn } from "@core/utils/cn.js"; + +const Tabs = TabsPrimitive.Root; + +const TabsList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsList.displayName = TabsPrimitive.List.displayName; + +const TabsTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsTrigger.displayName = TabsPrimitive.Trigger.displayName; + +const TabsContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsContent.displayName = TabsPrimitive.Content.displayName; + +export { Tabs, TabsList, TabsTrigger, TabsContent }; diff --git a/src/components/UI/Toast.tsx b/src/components/UI/Toast.tsx new file mode 100644 index 00000000..c03d53ef --- /dev/null +++ b/src/components/UI/Toast.tsx @@ -0,0 +1,128 @@ +import * as React from "react"; +import * as ToastPrimitives from "@radix-ui/react-toast"; +import { VariantProps, cva } from "class-variance-authority"; +import { X } from "lucide-react"; + +import { cn } from "@core/utils/cn.js"; + +const ToastProvider = ToastPrimitives.Provider; + +const ToastViewport = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +ToastViewport.displayName = ToastPrimitives.Viewport.displayName; + +const toastVariants = cva( + "data-[swipe=move]:transition-none grow-1 group relative pointer-events-auto flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full mt-4 data-[state=closed]:slide-out-to-right-full dark:border-slate-700 last:mt-0 sm:last:mt-4", + { + variants: { + variant: { + default: + "bg-white border-slate-200 dark:bg-slate-800 dark:border-slate-700", + destructive: + "group destructive bg-red-600 text-white border-red-600 dark:border-red-600" + } + }, + defaultVariants: { + variant: "default" + } + } +); + +const Toast = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, variant, ...props }, ref) => { + return ( + + ); +}); +Toast.displayName = ToastPrimitives.Root.displayName; + +const ToastAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +ToastAction.displayName = ToastPrimitives.Action.displayName; + +const ToastClose = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +ToastClose.displayName = ToastPrimitives.Close.displayName; + +const ToastTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +ToastTitle.displayName = ToastPrimitives.Title.displayName; + +const ToastDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +ToastDescription.displayName = ToastPrimitives.Description.displayName; + +type ToastProps = React.ComponentPropsWithoutRef; + +type ToastActionElement = React.ReactElement; + +export { + type ToastProps, + type ToastActionElement, + ToastProvider, + ToastViewport, + Toast, + ToastTitle, + ToastDescription, + ToastClose, + ToastAction +}; diff --git a/src/components/UI/Tooltip.tsx b/src/components/UI/Tooltip.tsx new file mode 100644 index 00000000..6f64fc18 --- /dev/null +++ b/src/components/UI/Tooltip.tsx @@ -0,0 +1,29 @@ +import * as React from "react"; +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; + +import { cn } from "@core/utils/cn.js"; + +const TooltipProvider = TooltipPrimitive.Provider; + +const Tooltip = ({ ...props }) => ; +Tooltip.displayName = TooltipPrimitive.Tooltip.displayName; + +const TooltipTrigger = TooltipPrimitive.Trigger; + +const TooltipContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + +)); +TooltipContent.displayName = TooltipPrimitive.Content.displayName; + +export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }; diff --git a/src/components/UI/Typography/Blockquote.tsx b/src/components/UI/Typography/Blockquote.tsx new file mode 100644 index 00000000..f2c739c0 --- /dev/null +++ b/src/components/UI/Typography/Blockquote.tsx @@ -0,0 +1,9 @@ +export interface BlockquoteProps { + children: React.ReactNode; +} + +export const BlockQuote = ({ children }: BlockquoteProps): JSX.Element => ( +
    + {children} +
    +); diff --git a/src/components/UI/Typography/Code.tsx b/src/components/UI/Typography/Code.tsx new file mode 100644 index 00000000..2925dfdc --- /dev/null +++ b/src/components/UI/Typography/Code.tsx @@ -0,0 +1,9 @@ +export interface CodeProps { + children: React.ReactNode; +} + +export const Code = ({ children }: CodeProps): JSX.Element => ( + + {children} + +); diff --git a/src/components/UI/Typography/H1.tsx b/src/components/UI/Typography/H1.tsx new file mode 100644 index 00000000..dd859347 --- /dev/null +++ b/src/components/UI/Typography/H1.tsx @@ -0,0 +1,9 @@ +export interface H1Props { + children: React.ReactNode; +} + +export const H1 = ({ children }: H1Props): JSX.Element => ( +

    + {children} +

    +); diff --git a/src/components/UI/Typography/H2.tsx b/src/components/UI/Typography/H2.tsx new file mode 100644 index 00000000..fc2afcc3 --- /dev/null +++ b/src/components/UI/Typography/H2.tsx @@ -0,0 +1,9 @@ +export interface H2Props { + children: React.ReactNode; +} + +export const H2 = ({ children }: H2Props): JSX.Element => ( +

    + {children} +

    +); diff --git a/src/components/UI/Typography/H3.tsx b/src/components/UI/Typography/H3.tsx new file mode 100644 index 00000000..56eedaaa --- /dev/null +++ b/src/components/UI/Typography/H3.tsx @@ -0,0 +1,9 @@ +export interface H3Props { + children: React.ReactNode; +} + +export const H3 = ({ children }: H3Props): JSX.Element => ( +

    + {children} +

    +); diff --git a/src/components/UI/Typography/H4.tsx b/src/components/UI/Typography/H4.tsx new file mode 100644 index 00000000..55b57bd8 --- /dev/null +++ b/src/components/UI/Typography/H4.tsx @@ -0,0 +1,17 @@ +import { cn } from "@app/core/utils/cn.js"; + +export interface H4Props { + className?: string; + children: React.ReactNode; +} + +export const H4 = ({ className, children }: H4Props): JSX.Element => ( +

    + {children} +

    +); diff --git a/src/components/UI/Typography/Link.tsx b/src/components/UI/Typography/Link.tsx new file mode 100644 index 00000000..af1a90ac --- /dev/null +++ b/src/components/UI/Typography/Link.tsx @@ -0,0 +1,15 @@ +export interface LinkProps { + href: string; + children: React.ReactNode; +} + +export const Link = ({ href, children }: LinkProps): JSX.Element => ( + + {children} + +); diff --git a/src/components/UI/Typography/P.tsx b/src/components/UI/Typography/P.tsx new file mode 100644 index 00000000..9464e1c1 --- /dev/null +++ b/src/components/UI/Typography/P.tsx @@ -0,0 +1,7 @@ +export interface PProps { + children: React.ReactNode; +} + +export const P = ({ children }: PProps): JSX.Element => ( +

    {children}

    +); diff --git a/src/components/UI/Typography/Subtle.tsx b/src/components/UI/Typography/Subtle.tsx new file mode 100644 index 00000000..60c45155 --- /dev/null +++ b/src/components/UI/Typography/Subtle.tsx @@ -0,0 +1,7 @@ +export interface SubtleProps { + children: React.ReactNode; +} + +export const Subtle = ({ children }: SubtleProps): JSX.Element => ( +

    {children}

    +); diff --git a/src/components/Widgets/BatteryWidget.tsx b/src/components/Widgets/BatteryWidget.tsx deleted file mode 100644 index a967b29f..00000000 --- a/src/components/Widgets/BatteryWidget.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { useEffect, useState } from "react"; -import prettyMilliseconds from "pretty-ms"; -import { useDevice } from "@core/providers/useDevice.js"; -import { Battery100Icon, ClockIcon } from "@heroicons/react/24/outline"; - -export interface BatteryWidgetProps { - batteryLevel: number; - voltage: number; -} - -export const BatteryWidget = ({ - batteryLevel, - voltage -}: BatteryWidgetProps): JSX.Element => { - const { nodes, hardware } = useDevice(); - - const [timeRemaining, setTimeRemaining] = useState("Unknown"); - - useEffect(() => { - const stats = nodes.find( - (n) => n.data.num === hardware.myNodeNum - )?.deviceMetrics; - - if (stats) { - let currentStat: number | undefined = undefined; - let currentTime = new Date(); - let previousStat: number | undefined = undefined; - let previousTime = new Date(); - for (const stat of [...stats].reverse()) { - if (stat.metric.batteryLevel) { - if (!currentStat) { - currentStat = stat.metric.batteryLevel; - currentTime = stat.timestamp; - } else { - previousStat = stat.metric.batteryLevel; - previousTime = stat.timestamp; - break; - } - } - } - - if (currentStat && previousStat) { - const timeDiff = currentTime.getTime() - previousTime.getTime(); - const statDiff = Math.abs(currentStat - previousStat); - if (statDiff !== 0) { - //convert to ms/% - const msPerPercent = timeDiff / statDiff; - const formatted = prettyMilliseconds( - (100 - currentStat) * msPerPercent - ); - setTimeRemaining(formatted); - } - } else setTimeRemaining("Unknown"); - } - }, [hardware.myNodeNum, nodes]); - - return ( -
    -
    - -
    -
    -

    Battery State

    -
    -

    {batteryLevel}%

    -
    -
    -
    -
    -
    - ); -}; diff --git a/src/components/Widgets/DeviceWidget.tsx b/src/components/Widgets/DeviceWidget.tsx deleted file mode 100644 index b1eadfe3..00000000 --- a/src/components/Widgets/DeviceWidget.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Button } from "@components/form/Button.js"; -import { Hashicon } from "@emeraldpay/hashicon-react"; -import { XCircleIcon } from "@heroicons/react/24/outline"; - -export interface DeviceWidgetProps { - name: string; - nodeNum: string; - disconnected: boolean; - disconnect: () => void; - reconnect: () => void; -} - -export const DeviceWidget = ({ - name, - nodeNum, - disconnected, - disconnect, - reconnect -}: DeviceWidgetProps): JSX.Element => { - return ( -
    -
    -
    - -
    -
    - - {name} - -
    - -
    -
    -
    -
    - ); -}; diff --git a/src/components/Widgets/PeersWidget.tsx b/src/components/Widgets/PeersWidget.tsx deleted file mode 100644 index 6d507317..00000000 --- a/src/components/Widgets/PeersWidget.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { useDevice } from "@core/providers/useDevice.js"; -import { IconButton } from "@components/form/IconButton.js"; -import { Mono } from "@components/generic/Mono.js"; -import { - EllipsisHorizontalIcon, - UserGroupIcon -} from "@heroicons/react/24/outline"; -import type { Protobuf } from "@meshtastic/meshtasticjs"; - -export interface PeersWidgetProps { - peers: Protobuf.NodeInfo[]; -} - -export const PeersWidget = ({ peers }: PeersWidgetProps): JSX.Element => { - const { setActivePage } = useDevice(); - - return ( -
    -
    - -
    -
    -

    Peers

    -
    - {peers.length > 0 ? ( -

    - {`${peers.length} ${peers.length > 1 ? "Peers" : "Peer"}`} -

    - ) : ( - None Discovered. - )} -
    -
    - { - setActivePage("peers"); - }} - icon={} - /> -
    - ); -}; diff --git a/src/components/Widgets/PositionWidget.tsx b/src/components/Widgets/PositionWidget.tsx deleted file mode 100644 index 961d426a..00000000 --- a/src/components/Widgets/PositionWidget.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { MapPinIcon } from "@heroicons/react/24/outline"; - -export interface PositionWidgetProps { - grid: string; -} - -export const PositionWidget = ({ grid }: PositionWidgetProps): JSX.Element => { - return ( -
    -
    - -
    -
    -

    Current Location

    -
    -

    {grid}

    -
    -
    -
    - ); -}; diff --git a/src/components/form/BitwiseSelect.tsx b/src/components/form/BitwiseSelect.tsx index 4265ac1e..89fbe052 100644 --- a/src/components/form/BitwiseSelect.tsx +++ b/src/components/form/BitwiseSelect.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import { bitwiseDecode, bitwiseEncode, enumLike } from "@core/utils/bitwise.js"; import { InfoWrapper } from "@components/form/InfoWrapper.js"; -import { Listbox } from "@headlessui/react"; +// import { Listbox } from "@headlessui/react"; import { Protobuf } from "@meshtastic/meshtasticjs"; export interface BitwiseSelectProps { @@ -50,7 +50,7 @@ export const BitwiseSelect = ({ return ( - { onChange(bitwiseEncode(value)); @@ -71,7 +71,7 @@ export const BitwiseSelect = ({ ))} - + */} ); }; diff --git a/src/components/form/Button.tsx b/src/components/form/Button.tsx deleted file mode 100644 index b129dc30..00000000 --- a/src/components/form/Button.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import type { ButtonHTMLAttributes, ComponentType, SVGProps } from "react"; - -export interface ButtonProps extends ButtonHTMLAttributes { - size?: "sm" | "md" | "lg"; -} - -export const Button = ({ - size = "md", - children, - disabled, - className, - ...rest -}: ButtonProps): JSX.Element => { - return ( - - ); -}; diff --git a/src/components/form/Form.tsx b/src/components/form/Form.tsx deleted file mode 100644 index 6cbdc033..00000000 --- a/src/components/form/Form.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import type { FormEvent, HTMLProps } from "react"; - -export interface FormProps extends HTMLProps { - onSubmit?: (event: FormEvent) => Promise; -} - -export const Form = ({ - children, - onSubmit, - ...props -}: FormProps): JSX.Element => { - return ( -
    -
    {children}
    -
    - ); -}; diff --git a/src/components/form/IconButton.tsx b/src/components/form/IconButton.tsx deleted file mode 100644 index d765d9af..00000000 --- a/src/components/form/IconButton.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import type { ButtonHTMLAttributes } from "react"; - -export interface IconButtonProps - extends ButtonHTMLAttributes { - size?: "sm" | "md" | "lg"; - icon?: JSX.Element; -} - -export const IconButton = ({ - size = "md", - icon, - disabled, - className, - ...rest -}: IconButtonProps): JSX.Element => { - return ( - - ); -}; diff --git a/src/components/form/InfoWrapper.tsx b/src/components/form/InfoWrapper.tsx index d9d0bcc1..198e2555 100644 --- a/src/components/form/InfoWrapper.tsx +++ b/src/components/form/InfoWrapper.tsx @@ -1,4 +1,4 @@ -import { ExclamationCircleIcon } from "@heroicons/react/24/outline"; +import { AlertCircleIcon } from "lucide-react"; import type { ReactNode } from "react"; export interface InfoWrapperProps { @@ -26,13 +26,13 @@ export const InfoWrapper = ({ {children} {error && (
    - +
    )} {description && (

    {description}

    )} - {error &&

    {error}

    } + {error &&

    {error}

    }
    ); }; diff --git a/src/components/form/Input.tsx b/src/components/form/Input.tsx index 2cbdd8e2..1c7a9acf 100644 --- a/src/components/form/Input.tsx +++ b/src/components/form/Input.tsx @@ -1,6 +1,6 @@ import { forwardRef, InputHTMLAttributes } from "react"; import { InfoWrapper, InfoWrapperProps } from "@components/form/InfoWrapper.js"; -import { ExclamationCircleIcon } from "@heroicons/react/24/outline"; +import { AlertCircleIcon } from "lucide-react"; export interface InputProps extends InputHTMLAttributes, @@ -63,7 +63,7 @@ export const Input = forwardRef(function Input( )} {error && (
    - +
    )} diff --git a/src/components/form/Select.tsx b/src/components/form/Select.tsx deleted file mode 100644 index 1594f5c6..00000000 --- a/src/components/form/Select.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { forwardRef, SelectHTMLAttributes } from "react"; - -import { InfoWrapper, InfoWrapperProps } from "@components/form/InfoWrapper.js"; - -export interface SelectProps - extends SelectHTMLAttributes, - Omit { - options?: string[]; - action?: { - icon: JSX.Element; - action: () => void; - }; -} - -export const Select = forwardRef(function Input( - { - label, - description, - options, - action, - disabled, - error, - children, - ...rest - }: SelectProps, - ref -) { - return ( - -
    - - {action && ( - - )} -
    -
    - ); -}); diff --git a/src/components/form/Toggle.tsx b/src/components/form/Toggle.tsx index d28047bc..4b1c2d17 100644 --- a/src/components/form/Toggle.tsx +++ b/src/components/form/Toggle.tsx @@ -1,11 +1,11 @@ -import { Switch } from "@headlessui/react"; +import { Switch } from "../UI/Switch.js"; +import { InfoWrapper } from "./InfoWrapper.js"; export interface ToggleProps { checked: boolean; label?: string; description?: string; disabled?: boolean; - className?: string; onChange?: (checked: boolean) => void; } @@ -14,44 +14,15 @@ export const Toggle = ({ label, description, disabled, - className, onChange }: ToggleProps): JSX.Element => { return ( - - - {label && ( - - {label} - - )} - {description && ( - - {description} - - )} - + - - - + onCheckedChange={onChange} + /> + ); }; diff --git a/src/components/generic/Dialog.tsx b/src/components/generic/Dialog.tsx deleted file mode 100644 index 69f8dd98..00000000 --- a/src/components/generic/Dialog.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { IconButton } from "@components/form/IconButton.js"; -import { Dialog as DialogUI } from "@headlessui/react"; -import { XMarkIcon } from "@heroicons/react/24/outline"; -import { ThemeController } from "@components/generic/ThemeController.js"; -import { Blur } from "@components/generic/Blur.js"; -import type { ReactNode } from "react"; - -export interface DialogProps { - title: string; - description: string; - isOpen: boolean; - close: () => void; - children: ReactNode; -} - -export const Dialog = ({ - title, - description, - isOpen, - close, - children -}: DialogProps): JSX.Element => { - return ( - - - -
    - -
    -
    -

    {title}

    -
    {description}
    -
    - } - /> -
    - -
    {children}
    -
    -
    -
    -
    - ); -}; diff --git a/src/components/generic/TabbedContent.tsx b/src/components/generic/TabbedContent.tsx deleted file mode 100644 index fdcc0d29..00000000 --- a/src/components/generic/TabbedContent.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { Fragment } from "react"; -import { Mono } from "@components/generic/Mono"; -import { Tab } from "@headlessui/react"; - -export interface TabType { - label: string; - icon?: JSX.Element; - element: () => JSX.Element; - disabled?: boolean; - disabledMessage?: string; - disabledLink?: string; -} - -export interface TabbedCOntentAction { - icon: JSX.Element; - action: () => void; -} - -export interface TabbedContentProps { - tabs: TabType[]; - actions?: TabbedCOntentAction[]; -} - -export const TabbedContent = ({ - tabs, - actions -}: TabbedContentProps): JSX.Element => { - return ( - - - {tabs.map((entry, index) => ( - - {({ selected }) => ( -
    - {entry.icon && ( -
    {entry.icon}
    - )} - {entry.label} -
    - )} -
    - ))} -
    - {actions?.map((action, index) => ( -
    - {action.icon} -
    - ))} -
    -
    - - {tabs.map((entry, index) => ( - - {!entry.disabled ? ( - - ) : ( -
    - - {entry.disabledMessage || "This tab is disabled"}.{" "} - {entry.disabledLink && ( - <> - Click{" "} - - here - {" "} - for more information. - - )} - -
    - )} -
    - ))} -
    -
    - ); -}; diff --git a/src/components/generic/Table/index.tsx b/src/components/generic/Table/index.tsx index 884aa66a..71d96ce1 100755 --- a/src/components/generic/Table/index.tsx +++ b/src/components/generic/Table/index.tsx @@ -1,4 +1,4 @@ -import { ChevronUpIcon } from "@heroicons/react/24/outline"; +import { ChevronUpIcon } from "lucide-react"; export interface TableProps { headings: Heading[]; @@ -28,7 +28,9 @@ export const Table = ({ headings, rows }: TableProps): JSX.Element => { >
    {heading.title} - {heading.sortable && } + {heading.sortable && ( + + )}
    ))} diff --git a/src/components/generic/VerticalTabbedContent.tsx b/src/components/generic/VerticalTabbedContent.tsx deleted file mode 100644 index d9010eb4..00000000 --- a/src/components/generic/VerticalTabbedContent.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { Fragment } from "react"; -import { Mono } from "@components/generic/Mono"; -import { Tab } from "@headlessui/react"; - -export interface TabType { - label: string; - element: () => JSX.Element; - disabled?: boolean; -} - -export interface TabbedContentProps { - tabs: TabType[]; -} - -export const VerticalTabbedContent = ({ - tabs -}: TabbedContentProps): JSX.Element => { - return ( - - - {tabs.map((tab, index) => ( - - {({ selected }) => ( -
    - {tab.label} - - 3 - -
    - )} -
    - ))} -
    - - {tabs.map((tab, index) => ( - - - - ))} - -
    - ); -}; diff --git a/src/core/providers/useDevice.ts b/src/core/providers/useDevice.ts deleted file mode 100644 index 020e5dfb..00000000 --- a/src/core/providers/useDevice.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { createContext, useContext } from "react"; - -import type { Device } from "@core/stores/deviceStore.js"; - -export const DeviceContext = createContext(undefined); - -export const useDevice = (): Device => { - const context = useContext(DeviceContext); - if (context === undefined) { - throw new Error("useDevice must be used within a DeviceProvider"); - } - return context; -}; diff --git a/src/core/stores/appStore.ts b/src/core/stores/appStore.ts index 4dc6cfae..4ccc3c54 100644 --- a/src/core/stores/appStore.ts +++ b/src/core/stores/appStore.ts @@ -4,7 +4,7 @@ import { create } from "zustand"; export interface RasterSource { enabled: boolean; title: string; - tiles: string[]; + tiles: string; tileSize: number; } @@ -27,6 +27,7 @@ interface AppState { commandPaletteOpen: boolean; darkMode: boolean; accent: accentColor; + connectDialogOpen: boolean; setRasterSources: (sources: RasterSource[]) => void; addRasterSource: (source: RasterSource) => void; @@ -38,6 +39,7 @@ interface AppState { setCommandPaletteOpen: (open: boolean) => void; setDarkMode: (enabled: boolean) => void; setAccent: (color: accentColor) => void; + setConnectDialogOpen: (open: boolean) => void; } export const useAppStore = create()((set) => ({ @@ -46,8 +48,9 @@ export const useAppStore = create()((set) => ({ currentPage: "messages", rasterSources: [], commandPaletteOpen: false, - darkMode: true, + darkMode: false, accent: "orange", + connectDialogOpen: false, setRasterSources: (sources: RasterSource[]) => { set( @@ -102,5 +105,12 @@ export const useAppStore = create()((set) => ({ draft.accent = color; }) ); + }, + setConnectDialogOpen: (open: boolean) => { + set( + produce((draft) => { + draft.connectDialogOpen = open; + }) + ); } })); diff --git a/src/core/stores/deviceStore.ts b/src/core/stores/deviceStore.ts index a938c0a8..f6638e80 100644 --- a/src/core/stores/deviceStore.ts +++ b/src/core/stores/deviceStore.ts @@ -1,16 +1,11 @@ -import { createContext } from "react"; +import { createContext, useContext } from "react"; import { produce } from "immer"; import { create } from "zustand"; import { Protobuf, Types } from "@meshtastic/meshtasticjs"; -export type Page = - | "messages" - | "map" - | "config" - | "channels" - | "peers"; +export type Page = "messages" | "map" | "config" | "channels" | "peers"; export interface MessageWithState extends Types.PacketMetadata { state: MessageState; @@ -51,7 +46,12 @@ export interface processPacketParams { time: number; } -export type DialogVariant = "import" | "QR" | "shutdown" | "reboot"; +export type DialogVariant = + | "import" + | "QR" + | "shutdown" + | "reboot" + | "deviceName"; export interface Device { id: number; @@ -78,6 +78,7 @@ export interface Device { QR: boolean; shutdown: boolean; reboot: boolean; + deviceName: boolean; }; setReady(ready: boolean): void; @@ -152,7 +153,8 @@ export const useDeviceStore = create((set, get) => ({ import: false, QR: false, shutdown: false, - reboot: false + reboot: false, + deviceName: false }, pendingSettingsChanges: false, messageDraft: "", @@ -610,3 +612,11 @@ export const useDeviceStore = create((set, get) => ({ })); export const DeviceContext = createContext(undefined); + +export const useDevice = (): Device => { + const context = useContext(DeviceContext); + if (context === undefined) { + throw new Error("useDevice must be used within a DeviceProvider"); + } + return context; +}; diff --git a/src/core/subscriptions.ts b/src/core/subscriptions.ts index 05084134..13d5a33e 100644 --- a/src/core/subscriptions.ts +++ b/src/core/subscriptions.ts @@ -1,5 +1,3 @@ -import { toast } from "react-hot-toast"; - import type { Device } from "@core/stores/deviceStore.js"; import { Protobuf, Types } from "@meshtastic/meshtasticjs"; @@ -22,24 +20,15 @@ export const subscribeAll = ( if (routingPacket.data.variant.value === Protobuf.Routing_Error.NONE) { return; } - toast.error( - `Routing error: ${ - Protobuf.Routing_Error[routingPacket.data.variant.value] - }`, - { - icon: "❌" - } - ); + console.log(`Routing Error: ${routingPacket.data.variant.value}`); break; case "routeReply": - toast(`Route Reply: ${routingPacket.data.variant.value}`, { - icon: "✅" - }); + console.log(`Route Reply: ${routingPacket.data.variant.value}`); + break; case "routeRequest": - toast(`Route Request: ${routingPacket.data.variant.value}`, { - icon: "✅" - }); + console.log(`Route Request: ${routingPacket.data.variant.value}`); + break; } }); @@ -82,9 +71,9 @@ export const subscribeAll = ( }); connection.events.onNodeInfoPacket.subscribe((nodeInfo) => { - toast(`New Node Discovered: ${nodeInfo.user?.shortName ?? "UNK"}`, { - icon: "🔎" - }); + // toast(`New Node Discovered: ${nodeInfo.user?.shortName ?? "UNK"}`, { + // icon: "🔎" + // }); device.addNodeInfo(nodeInfo); }); diff --git a/src/core/utils/cn.ts b/src/core/utils/cn.ts new file mode 100644 index 00000000..cec6ac9e --- /dev/null +++ b/src/core/utils/cn.ts @@ -0,0 +1,6 @@ +import { ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/src/core/utils/selectEnumOptions.tsx b/src/core/utils/selectEnumOptions.tsx deleted file mode 100644 index 926fc115..00000000 --- a/src/core/utils/selectEnumOptions.tsx +++ /dev/null @@ -1,13 +0,0 @@ -export const renderOptions = (enumValue: { - [s: string]: string | number; -}): JSX.Element[] => { - const optionsEnumValues = enumValue - ? Object.entries(enumValue).filter((value) => typeof value[1] === "number") - : []; - - return optionsEnumValues.map(([name, value], index) => ( - - )); -}; diff --git a/src/index.css b/src/index.css index b2692767..8a35066d 100644 --- a/src/index.css +++ b/src/index.css @@ -3,7 +3,7 @@ @tailwind utilities; :root { - --backgroundPrimary: #f5f5f6; + --backgroundPrimary: #ffffff; --backgroundSecondary: #e6e9ed; --textPrimary: #111132; --textSecondary: #64748b; @@ -15,7 +15,7 @@ } [data-theme="dark"] { - --backgroundPrimary: #2d2d30; + --backgroundPrimary: #0f172a; --backgroundSecondary: #363638; --textPrimary: #ebebeb; --textSecondary: #bdbdbd; diff --git a/src/pages/Channels.tsx b/src/pages/Channels.tsx index 466cdb83..96eaeba3 100644 --- a/src/pages/Channels.tsx +++ b/src/pages/Channels.tsx @@ -1,39 +1,82 @@ -import { TabbedContent, TabType } from "@components/generic/TabbedContent"; +import { Sidebar } from "@app/components/Sidebar.js"; +import { PageLayout } from "@app/components/Topbar.js"; +import { cn } from "@app/core/utils/cn.js"; import { Channel } from "@components/PageComponents/Channel.js"; -import { useDevice } from "@core/providers/useDevice.js"; -import { - ArrowDownOnSquareStackIcon, - QrCodeIcon -} from "@heroicons/react/24/outline"; -import { Protobuf } from "@meshtastic/meshtasticjs"; +import { useDevice } from "@core/stores/deviceStore.js"; +import { QrCodeIcon, ImportIcon } from "lucide-react"; +import { Protobuf, Types } from "@meshtastic/meshtasticjs"; +import { SidebarSection } from "@app/components/UI/Sidebar/SidebarSection.js"; +import { useState } from "react"; +import { Button } from "@app/components/UI/Button.js"; +import { SidebarButton } from "@app/components/UI/Sidebar/sidebarButton.js"; + +export const getChannelName = (channel: Protobuf.Channel) => + channel.settings?.name.length + ? channel.settings?.name + : channel.index === 0 + ? "Primary" + : `Ch ${channel.index}`; export const ChannelsPage = (): JSX.Element => { const { channels, setDialogOpen } = useDevice(); - - const tabs: TabType[] = channels.map((channel) => { - return { - label: channel.config.settings?.name.length - ? channel.config.settings.name - : channel.config.role === Protobuf.Channel_Role.PRIMARY - ? "Primary" - : `Channel: ${channel.config.index}`, - element: () => - }; - }); + const [activeChannel, setActiveChannel] = useState( + Types.ChannelNumber.PRIMARY + ); return ( - , - action: () => setDialogOpen("import", true) - }, - { - icon: , - action: () => setDialogOpen("QR", true) - } - ]} - /> + <> + + + {channels.map((channel) => ( + + } + onClick={() => setActiveChannel(channel.config.index)} + /> + ))} + + + + {channels.map( + (channel) => + channel.config.index === activeChannel && ( + + ) + )} + + ); }; diff --git a/src/pages/Config/AppConfig.tsx b/src/pages/Config/AppConfig.tsx deleted file mode 100644 index 6e6e3c5b..00000000 --- a/src/pages/Config/AppConfig.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { Fragment } from "react"; -import { Map } from "@components/PageComponents/AppConfig/Map.js"; -import { Tab } from "@headlessui/react"; - -export const AppConfig = (): JSX.Element => { - const configSections = [ - { - label: "Map", - element: Map - } - ]; - - return ( - - - {configSections.map((Config, index) => ( - - {({ selected }) => ( -
    - {Config.label} -
    - )} -
    - ))} -
    - - {configSections.map((Config, index) => ( - - - - ))} - -
    - ); -}; diff --git a/src/pages/Config/DeviceConfig.tsx b/src/pages/Config/DeviceConfig.tsx index 4d81345c..f16ff8f8 100644 --- a/src/pages/Config/DeviceConfig.tsx +++ b/src/pages/Config/DeviceConfig.tsx @@ -6,26 +6,22 @@ import { Display } from "@components/PageComponents/Config/Display.js"; import { LoRa } from "@components/PageComponents/Config/LoRa.js"; import { Position } from "@components/PageComponents/Config/Position.js"; import { Power } from "@components/PageComponents/Config/Power.js"; -import { User } from "@components/PageComponents/Config/User.js"; -import { useDevice } from "@core/providers/useDevice.js"; -import { Tab } from "@headlessui/react"; -import { ChevronRightIcon, HomeIcon } from "@heroicons/react/24/outline"; -import { Button } from "@components/form/Button.js"; -import { CheckIcon } from "@primer/octicons-react"; -import { NavBar } from "@app/Nav/NavBar.js"; -import { VerticalTabbedContent } from "@app/components/generic/VerticalTabbedContent.js"; +import { useDevice } from "@core/stores/deviceStore.js"; +import { + Tabs, + TabsContent, + TabsList, + TabsTrigger +} from "@components/UI/Tabs.js"; export const DeviceConfig = (): JSX.Element => { - const { hardware, workingConfig, connection } = useDevice(); + const { hardware } = useDevice(); const tabs = [ - { - label: "User", - element: User - }, { label: "Device", - element: Device + element: Device, + count: 0 }, { label: "Position", @@ -55,23 +51,23 @@ export const DeviceConfig = (): JSX.Element => { ]; return ( -
    - { - await connection?.setConfig(config); - }); - await connection?.commitEditSettings(); - } - } - ]} - /> - - -
    + + + {tabs.map((tab) => ( + + {tab.label} + + ))} + + {tabs.map((tab) => ( + + + + ))} + ); }; diff --git a/src/pages/Config/ModuleConfig.tsx b/src/pages/Config/ModuleConfig.tsx index 1f5b84fa..f44fb817 100644 --- a/src/pages/Config/ModuleConfig.tsx +++ b/src/pages/Config/ModuleConfig.tsx @@ -7,9 +7,13 @@ import { RangeTest } from "@components/PageComponents/ModuleConfig/RangeTest.js" import { Serial } from "@components/PageComponents/ModuleConfig/Serial.js"; import { StoreForward } from "@components/PageComponents/ModuleConfig/StoreForward.js"; import { Telemetry } from "@components/PageComponents/ModuleConfig/Telemetry.js"; -import { useDevice } from "@app/core/providers/useDevice.js"; -import { NavBar } from "@app/Nav/NavBar.js"; -import { VerticalTabbedContent } from "@app/components/generic/VerticalTabbedContent.js"; +import { useDevice } from "@app/core/stores/deviceStore.js"; +import { + Tabs, + TabsContent, + TabsList, + TabsTrigger +} from "@components/UI/Tabs.js"; export const ModuleConfig = (): JSX.Element => { const { workingModuleConfig, connection } = useDevice(); @@ -24,11 +28,11 @@ export const ModuleConfig = (): JSX.Element => { element: Serial }, { - label: "External Notification", + label: "Ext Notif", element: ExternalNotification }, { - label: "Store & Forward", + label: "S&F", element: StoreForward }, { @@ -40,33 +44,29 @@ export const ModuleConfig = (): JSX.Element => { element: Telemetry }, { - label: "Canned Message", + label: "Canned", element: CannedMessage }, { - label: "Audio Config", + label: "Audio", element: Audio } ]; return ( -
    - { - await connection?.setModuleConfig(moduleConfig); - }); - await connection?.commitEditSettings(); - } - } - ]} - /> - - -
    + + + {tabs.map((tab) => ( + + {tab.label} + + ))} + + {tabs.map((tab) => ( + + + + ))} + ); }; diff --git a/src/pages/Config/index.tsx b/src/pages/Config/index.tsx index b978590c..9332da0e 100644 --- a/src/pages/Config/index.tsx +++ b/src/pages/Config/index.tsx @@ -1,34 +1,69 @@ -import { TabbedContent, TabType } from "@components/generic/TabbedContent"; -import { useDevice } from "@core/providers/useDevice.js"; -import { - Cog8ToothIcon, - CubeTransparentIcon, - WindowIcon -} from "@heroicons/react/24/outline"; -import { AppConfig } from "@pages/Config/AppConfig.js"; +import { Sidebar } from "@app/components/Sidebar.js"; +import { SettingsIcon, BoxesIcon, SaveIcon } from "lucide-react"; import { DeviceConfig } from "@pages/Config/DeviceConfig.js"; import { ModuleConfig } from "@pages/Config/ModuleConfig.js"; +import { PageLayout } from "@app/components/Topbar.js"; +import { SidebarSection } from "@app/components/UI/Sidebar/SidebarSection.js"; +import { useState } from "react"; +import { useDevice } from "@app/core/stores/deviceStore.js"; +import { Button } from "@app/components/UI/Button.js"; +import { SidebarButton } from "@app/components/UI/Sidebar/sidebarButton.js"; export const ConfigPage = (): JSX.Element => { - const { connection, pendingSettingsChanges } = useDevice(); + const { workingConfig, workingModuleConfig, connection } = useDevice(); + const [activeConfigSection, setActiveConfigSection] = useState< + "device" | "module" + >("device"); - const tabs: TabType[] = [ - { - label: "Device Config", - icon: , - element: DeviceConfig - }, - { - label: "Module Config", - icon: , - element: ModuleConfig - }, - { - label: "App Config", - icon: , - element: AppConfig - } - ]; + return ( + <> + + + setActiveConfigSection("device")} + icon={SettingsIcon} + /> + setActiveConfigSection("module")} + icon={BoxesIcon} + /> + + + await connection?.setConfig(config) + ); + } else { + workingModuleConfig.map( + async (config) => await connection?.setModuleConfig(config) + ); + } - return ; + await connection?.commitEditSettings(); + } + } + ]} + > +
    + {activeConfigSection === "device" ? ( + + ) : ( + + )} +
    +
    + + ); }; diff --git a/src/pages/Map.tsx b/src/pages/Map.tsx index 21219162..8cca4ee8 100644 --- a/src/pages/Map.tsx +++ b/src/pages/Map.tsx @@ -1,64 +1,135 @@ import maplibregl from "maplibre-gl"; -import { Layer, Map, Marker, Source } from "react-map-gl"; -import { MapControlls } from "@components/PageComponents/Map/MapControlls.js"; +import { Layer, Map, Marker, Source, useMap } from "react-map-gl"; import { useAppStore } from "@core/stores/appStore.js"; -import { useDevice } from "@core/providers/useDevice.js"; +import { useDevice } from "@core/stores/deviceStore.js"; import { Hashicon } from "@emeraldpay/hashicon-react"; -import { MapPinIcon } from "@heroicons/react/24/outline"; +import { Sidebar } from "@app/components/Sidebar.js"; +import { PageLayout } from "@app/components/Topbar.js"; +import { + ZoomInIcon, + ZoomOutIcon, + BoxSelectIcon, + MapPinIcon, + EditIcon, + PlusIcon +} from "lucide-react"; +import { bbox, lineString } from "@turf/turf"; +import { SidebarSection } from "@app/components/UI/Sidebar/SidebarSection.js"; +import { Button } from "@app/components/UI/Button.js"; +import { SidebarButton } from "@app/components/UI/Sidebar/sidebarButton.js"; export const MapPage = (): JSX.Element => { const { nodes, waypoints } = useDevice(); const { rasterSources } = useAppStore(); + const { default: map } = useMap(); + + const getBBox = () => { + const nodesWithPosition = nodes.filter((n) => n.data.position?.latitudeI); + if (!nodesWithPosition.length) return; + const line = lineString( + nodesWithPosition.map((n) => [ + (n.data.position?.latitudeI ?? 0) / 1e7, + (n.data.position?.longitudeI ?? 0) / 1e7 + ]) + ); + const bounds = bbox(line); + const center = map?.cameraForBounds( + [ + [bounds[1], bounds[0]], + [bounds[3], bounds[2]] + ], + { padding: { top: 10, bottom: 10, left: 10, right: 10 } } + ); + if (center) map?.easeTo(center); + else if (nodesWithPosition.length === 1) + map?.easeTo({ + zoom: 12, + center: [ + (nodesWithPosition[0].data.position?.longitudeI ?? 0) / 1e7, + (nodesWithPosition[0].data.position?.latitudeI ?? 0) / 1e7 + ] + }); + }; return ( -
    - - - {waypoints.map((wp) => ( - -
    - -
    -
    - ))} - {rasterSources.map((source, index) => ( - - - - ))} - {nodes.map((n) => { - if (n.data.position?.latitudeI) { - return ( - - - - ); + <> + + + {rasterSources.map((source, index) => ( + + ))} + + + -
    + ]} + > + + {waypoints.map((wp) => ( + +
    + +
    +
    + ))} + {/* {rasterSources.map((source, index) => ( + + + + ))} */} + {nodes.map((n) => { + if (n.data.position?.latitudeI) { + return ( + + + + ); + } + })} +
    + + ); }; diff --git a/src/pages/Messages.tsx b/src/pages/Messages.tsx index f38afb11..d13b4a74 100644 --- a/src/pages/Messages.tsx +++ b/src/pages/Messages.tsx @@ -1,35 +1,71 @@ -import { TabbedContent, TabType } from "@components/generic/TabbedContent.js"; +import { Sidebar } from "@app/components/Sidebar.js"; +import { PageLayout } from "@app/components/Topbar.js"; import { ChannelChat } from "@components/PageComponents/Messages/ChannelChat.js"; -import { useDevice } from "@core/providers/useDevice.js"; -import { PencilIcon } from "@heroicons/react/24/outline"; -import { Protobuf } from "@meshtastic/meshtasticjs"; +import { useDevice } from "@core/stores/deviceStore.js"; +import { Hashicon } from "@emeraldpay/hashicon-react"; +import { HashIcon } from "lucide-react"; +import { Protobuf, Types } from "@meshtastic/meshtasticjs"; +import { SidebarSection } from "@app/components/UI/Sidebar/SidebarSection.js"; +import { useState } from "react"; +import { getChannelName } from "./Channels.js"; +import { SidebarButton } from "@app/components/UI/Sidebar/sidebarButton.js"; export const MessagesPage = (): JSX.Element => { - const { channels, setActivePage } = useDevice(); - - const tabs: TabType[] = channels.map((channel) => { - return { - label: channel.config.settings?.name.length - ? channel.config.settings?.name - : channel.config.index === 0 - ? "Primary" - : `Ch ${channel.config.index}`, - element: () => , - disabled: channel.config.role === Protobuf.Channel_Role.DISABLED - }; - }); + const { channels, nodes, hardware } = useDevice(); + const [activeChannel, setActiveChannel] = useState( + Types.ChannelNumber.PRIMARY + ); return ( -
    - , - action: () => setActivePage("channels") - } - ]} - /> -
    + <> + + + {channels + .filter((ch) => ch.config.role !== Protobuf.Channel_Role.DISABLED) + .map((channel) => ( + setActiveChannel(channel.config.index)} + element={} + /> + ))} + + + {nodes + .filter((n) => n.data.num !== hardware.myNodeNum) + .map((node) => ( + + } + /> + ))} + + + + {channels.map( + (channel) => + channel.config.index === activeChannel && ( + + ) + )} + + ); }; diff --git a/src/pages/Peers.tsx b/src/pages/Peers.tsx index 5ce953d3..01d97dd4 100644 --- a/src/pages/Peers.tsx +++ b/src/pages/Peers.tsx @@ -2,49 +2,57 @@ import { base16 } from "rfc4648"; import { Mono } from "@components/generic/Mono.js"; import { Table } from "@components/generic/Table"; import { TimeAgo } from "@components/generic/Table/tmp/TimeAgo.js"; -import { useDevice } from "@core/providers/useDevice.js"; +import { useDevice } from "@core/stores/deviceStore.js"; import { Hashicon } from "@emeraldpay/hashicon-react"; import { Protobuf } from "@meshtastic/meshtasticjs"; +import { Sidebar } from "@app/components/Sidebar.js"; export const PeersPage = (): JSX.Element => { const { connection, nodes } = useDevice(); return ( -
    - [ - , -

    - {node.data.user?.longName ?? node.data.user?.macaddr - ? `Meshtastic_${base16 - .stringify(node.data.user?.macaddr.subarray(4, 6) ?? []) - .toLowerCase()}` - : `UNK: ${node.data.num}`} -

    , + <> + +
    +
    [ + , +

    + {node.data.user?.longName ?? node.data.user?.macaddr + ? `Meshtastic_${base16 + .stringify(node.data.user?.macaddr.subarray(4, 6) ?? []) + .toLowerCase()}` + : `UNK: ${node.data.num}`} +

    , - {Protobuf.HardwareModel[node.data.user?.hwModel ?? 0]}, - - {base16 - .stringify(node.data.user?.macaddr ?? []) - .match(/.{1,2}/g) - ?.join(":") ?? "UNK"} - , - , - - {node.data.snr}db/ - {Math.min(Math.max((node.data.snr + 10) * 5, 0), 100)}%/ - {(node.data.snr + 10) * 5}raw - - ])} - /> - + {Protobuf.HardwareModel[node.data.user?.hwModel ?? 0]}, + + {base16 + .stringify(node.data.user?.macaddr ?? []) + .match(/.{1,2}/g) + ?.join(":") ?? "UNK"} + , + node.data.lastHeard === 0 ? ( +

    Never

    + ) : ( + + ), + + {node.data.snr}db/ + {Math.min(Math.max((node.data.snr + 10) * 5, 0), 100)}%/ + {(node.data.snr + 10) * 5}raw + + ])} + /> + + ); }; diff --git a/src/validation/config/device.ts b/src/validation/config/device.ts index 103fed46..fb389b23 100644 --- a/src/validation/config/device.ts +++ b/src/validation/config/device.ts @@ -19,4 +19,10 @@ export class DeviceValidation @IsInt() buzzerGpio: number; + + @IsEnum(Protobuf.Config_DeviceConfig_RebroadcastMode) + rebroadcastMode: Protobuf.Config_DeviceConfig_RebroadcastMode; + + @IsInt() + nodeInfoBroadcastSecs: number; } diff --git a/src/validation/config/lora.ts b/src/validation/config/lora.ts index efe7ab74..3ad22385 100644 --- a/src/validation/config/lora.ts +++ b/src/validation/config/lora.ts @@ -51,6 +51,9 @@ export class LoRaValidation @IsBoolean() sx126xRxBoostedGain: boolean; + @IsInt() + overrideFrequency: number; + @IsArray() ignoreIncoming: number[]; } diff --git a/src/validation/config/position.ts b/src/validation/config/position.ts index a0c07e0d..a807ad96 100644 --- a/src/validation/config/position.ts +++ b/src/validation/config/position.ts @@ -32,14 +32,4 @@ export class PositionValidation @IsInt() txGpio: number; - - // fixed position fields - @IsNumber() - fixedAlt: number; - - @IsNumber() - fixedLat: number; - - @IsNumber() - fixedLng: number; } diff --git a/src/validation/config/user.ts b/src/validation/config/user.ts deleted file mode 100644 index bcd9e43f..00000000 --- a/src/validation/config/user.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { IsBoolean, IsOptional, IsString, Length } from "class-validator"; - -import type { Protobuf } from "@meshtastic/meshtasticjs"; - -export class UserValidation - implements - Omit< - Protobuf.User, - keyof Protobuf.native.Message | "ID" | "macaddr" | "hwModel" - > -{ - @IsString() - @IsOptional() - id: string; - - @Length(2, 30) - longName: string; - - @Length(1, 4) - shortName: string; - - @IsBoolean() - isLicensed: boolean; -} diff --git a/src/validation/appConfig/map.ts b/src/validation/rasterSource.ts similarity index 75% rename from src/validation/appConfig/map.ts rename to src/validation/rasterSource.ts index 68eac3ed..0afeec77 100644 --- a/src/validation/appConfig/map.ts +++ b/src/validation/rasterSource.ts @@ -1,4 +1,4 @@ -import { IsArray, IsBoolean, IsNumber, IsString } from "class-validator"; +import { IsArray, IsBoolean, IsNumber, IsString, IsUrl } from "class-validator"; import type { RasterSource } from "@core/stores/appStore.js"; @@ -14,8 +14,8 @@ export class MapValidation_RasterSources implements RasterSource { @IsString() title: string; - // @IsUrl() - tiles: string[]; + @IsUrl() + tiles: string; @IsNumber() tileSize: number; diff --git a/tailwind.config.cjs b/tailwind.config.cjs index 1836670f..0f0f93b1 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -1,37 +1,44 @@ +const { fontFamily } = require("tailwindcss/defaultTheme"); + /** @type {import('tailwindcss').Config} */ module.exports = { + darkMode: ["class", '[data-theme="dark"]'], content: ["./index.html", "./src/**/*.{ts,tsx}"], theme: { - fontFamily: { - mono: [ - "Cascadia Code", - "ui-monospace", - "SFMono-Regular", - "Menlo", - "Monaco", - "Consolas", - "Liberation Mono", - "Courier New", - "monospace" - ] - }, - colors: { - transparent: "transparent", - current: "currentColor", - backgroundPrimary: "var(--backgroundPrimary)", - backgroundSecondary: "var(--backgroundSecondary)", - accent: "var(--accent)", - accentMuted: "var(--accentMuted)", - textPrimary: "var(--textPrimary)", - textSecondary: "var(--textSecondary)", - link: "var(--link)" - }, - brightness: { - hover: "var(--brighnessHover)", - press: "var(--brightnessPress)", - disabled: "var(--brightnessDisabled)" - }, - extend: {} + extend: { + fontFamily: { + mono: ["Cascadia Code", ...fontFamily.mono], + sans: ["Inter var", ...fontFamily.sans] + }, + keyframes: { + "accordion-down": { + from: { height: 0 }, + to: { height: "var(--radix-accordion-content-height)" } + }, + "accordion-up": { + from: { height: "var(--radix-accordion-content-height)" }, + to: { height: 0 } + } + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out" + }, + colors: { + backgroundPrimary: "var(--backgroundPrimary)", + backgroundSecondary: "var(--backgroundSecondary)", + accent: "var(--accent)", + accentMuted: "var(--accentMuted)", + textPrimary: "var(--textPrimary)", + textSecondary: "var(--textSecondary)", + link: "var(--link)" + }, + brightness: { + hover: "var(--brighnessHover)", + press: "var(--brightnessPress)", + disabled: "var(--brightnessDisabled)" + } + } }, - plugins: [require("@tailwindcss/forms")] + plugins: [require("@tailwindcss/forms"), require("tailwindcss-animate")] };