diff --git a/Gemfile.lock b/Gemfile.lock
index 32515384a5..147d3c5da0 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,7 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
- action_text-trix (2.1.16)
+ action_text-trix (2.1.17)
railties
actioncable (8.1.2)
actionpack (= 8.1.2)
@@ -115,7 +115,7 @@ GEM
delayed_job_active_record (4.1.11)
activerecord (>= 3.0, < 9.0)
delayed_job (>= 3.0, < 5)
- devise (5.0.2)
+ devise (5.0.3)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 7.0)
@@ -196,7 +196,7 @@ GEM
prism (>= 1.3.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
- json (2.19.1)
+ json (2.19.2)
jsonapi-renderer (0.2.2)
jwt (3.1.2)
base64
@@ -218,7 +218,7 @@ GEM
activesupport (>= 4)
railties (>= 4)
request_store (~> 1.0)
- loofah (2.25.0)
+ loofah (2.25.1)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
mail (2.9.0)
@@ -360,14 +360,14 @@ GEM
rspec-mocks (3.13.8)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
- rspec-rails (8.0.3)
+ rspec-rails (8.0.4)
actionpack (>= 7.2)
activesupport (>= 7.2)
railties (>= 7.2)
- rspec-core (~> 3.13)
- rspec-expectations (~> 3.13)
- rspec-mocks (~> 3.13)
- rspec-support (~> 3.13)
+ rspec-core (>= 3.13.0, < 5.0.0)
+ rspec-expectations (>= 3.13.0, < 5.0.0)
+ rspec-mocks (>= 3.13.0, < 5.0.0)
+ rspec-support (>= 3.13.0, < 5.0.0)
rspec-support (3.13.7)
rspec_junit_formatter (0.6.0)
rspec-core (>= 2, < 4, != 2.12.0)
@@ -409,7 +409,7 @@ GEM
useragent (0.16.11)
warden (1.2.9)
rack (>= 2.0.9)
- webmock (3.26.1)
+ webmock (3.26.2)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
diff --git a/bun.lock b/bun.lock
index cf82288f00..151944dcb8 100644
--- a/bun.lock
+++ b/bun.lock
@@ -5,8 +5,8 @@
"": {
"name": "farmbot-web-frontend",
"dependencies": {
- "@blueprintjs/core": "6.9.1",
- "@blueprintjs/select": "6.1.3",
+ "@blueprintjs/core": "6.10.0",
+ "@blueprintjs/select": "6.1.4",
"@monaco-editor/react": "4.7.0",
"@react-spring/three": "10.0.3",
"@react-three/drei": "10.7.7",
@@ -31,7 +31,7 @@
"farmbot": "15.9.3",
"fengari": "0.1.5",
"fengari-web": "0.1.4",
- "i18next": "25.8.17",
+ "i18next": "25.8.18",
"lodash": "4.17.23",
"markdown-it": "14.1.1",
"markdown-it-emoji": "3.0.0",
@@ -50,7 +50,7 @@
"redux": "5.0.1",
"redux-immutable-state-invariant": "2.1.0",
"redux-thunk": "3.1.0",
- "rollbar": "3.0.0",
+ "rollbar": "3.1.0",
"suncalc": "1.9.0",
"takeme": "0.12.0",
"three": "0.183.2",
@@ -59,7 +59,7 @@
},
"devDependencies": {
"@eslint/js": "10.0.1",
- "@happy-dom/global-registrator": "20.8.3",
+ "@happy-dom/global-registrator": "20.8.4",
"@react-three/eslint-plugin": "0.1.2",
"@testing-library/dom": "10.4.1",
"@testing-library/jest-dom": "6.9.1",
@@ -69,8 +69,8 @@
"@types/jest": "30.0.0",
"@types/readable-stream": "4.0.23",
"@types/suncalc": "1.9.2",
- "@typescript-eslint/eslint-plugin": "8.57.0",
- "@typescript-eslint/parser": "8.57.0",
+ "@typescript-eslint/eslint-plugin": "8.57.1",
+ "@typescript-eslint/parser": "8.57.1",
"eslint": "10.0.3",
"eslint-plugin-eslint-comments": "3.2.0",
"eslint-plugin-import": "2.32.0",
@@ -79,7 +79,7 @@
"eslint-plugin-promise": "7.2.1",
"eslint-plugin-react": "7.37.5",
"eslint-plugin-react-hooks": "7.0.1",
- "happy-dom": "20.8.3",
+ "happy-dom": "20.8.4",
"jest": "30.3.0",
"jest-canvas-mock": "2.5.2",
"jest-cli": "30.3.0",
@@ -178,13 +178,13 @@
"@bcoe/v8-coverage": ["@bcoe/v8-coverage@0.2.3", "", {}, "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw=="],
- "@blueprintjs/colors": ["@blueprintjs/colors@5.1.14", "", { "dependencies": { "tslib": "~2.6.2" } }, "sha512-Ak6NpUBc0nFpWxucYe7GgMwdcrlARX7yfSPxt4va7z2IM05peNh8OOZ2jQij5+sIgU6IoIkgILAqlQ8nNRhWww=="],
+ "@blueprintjs/colors": ["@blueprintjs/colors@5.1.15", "", { "dependencies": { "tslib": "~2.6.2" } }, "sha512-hxv11fKWwU707NaVWKUpJPOOCeinRC7zNhF2sX6vLN0UmDqHapItARTLW5DvwKTyPoDQQXUpX9yFwDlTQPbQ9w=="],
- "@blueprintjs/core": ["@blueprintjs/core@6.9.1", "", { "dependencies": { "@blueprintjs/colors": "^5.1.14", "@blueprintjs/icons": "^6.6.0", "@floating-ui/react": "^0.27.13", "@popperjs/core": "^2.11.8", "classnames": "^2.3.1", "normalize.css": "^8.0.1", "react-popper": "^2.3.0", "react-transition-group": "^4.4.5", "tslib": "~2.6.2", "use-sync-external-store": "^1.2.0" }, "peerDependencies": { "@types/react": "18", "react": "18", "react-dom": "18" }, "optionalPeers": ["@types/react"], "bin": { "upgrade-blueprint-2.0.0-rename": "scripts/upgrade-blueprint-2.0.0-rename.sh", "upgrade-blueprint-3.0.0-rename": "scripts/upgrade-blueprint-3.0.0-rename.sh" } }, "sha512-RmkIpN6dGch7ZCT8dkmbK80JgcIqV4w19SmzJTot7QKm7fDRsi2hcusV/LOvn7WgZvS9Z8URzIp357jAN9Nv5A=="],
+ "@blueprintjs/core": ["@blueprintjs/core@6.10.0", "", { "dependencies": { "@blueprintjs/colors": "^5.1.15", "@blueprintjs/icons": "^6.7.0", "@floating-ui/react": "^0.27.13", "@popperjs/core": "^2.11.8", "classnames": "^2.3.1", "normalize.css": "^8.0.1", "react-popper": "^2.3.0", "react-transition-group": "^4.4.5", "tslib": "~2.6.2", "use-sync-external-store": "^1.2.0" }, "peerDependencies": { "@types/react": "18", "react": "18", "react-dom": "18" }, "optionalPeers": ["@types/react"], "bin": { "upgrade-blueprint-2.0.0-rename": "scripts/upgrade-blueprint-2.0.0-rename.sh", "upgrade-blueprint-3.0.0-rename": "scripts/upgrade-blueprint-3.0.0-rename.sh" } }, "sha512-k6B8aeIwuH0ns2NAz4iWWcfv5/0v5Av9ePQXmNbs2VOdPSRn3X0l1mhyNBAjuuXHJV3OEENGE1HSbFi6f1NNJw=="],
- "@blueprintjs/icons": ["@blueprintjs/icons@6.6.0", "", { "dependencies": { "change-case": "^4.1.2", "classnames": "^2.3.1", "tslib": "~2.6.2" }, "peerDependencies": { "@types/react": "18", "react": "18", "react-dom": "18" }, "optionalPeers": ["@types/react"] }, "sha512-IaMTAAY554iqUCvfO9+okAnC/qprypQqkNOkBdAkID6lXud8PSyfJWdXneSBQnU/fHU1UA+7xILJ6Wr4wGoJGw=="],
+ "@blueprintjs/icons": ["@blueprintjs/icons@6.7.0", "", { "dependencies": { "change-case": "^4.1.2", "classnames": "^2.3.1", "tslib": "~2.6.2" }, "peerDependencies": { "@types/react": "18", "react": "18", "react-dom": "18" }, "optionalPeers": ["@types/react"] }, "sha512-wT8LjdXz5ogWYtWGsMpmLHbgg5Ml7rGcTg4QzHRL4NROaS2LEuNCNRQxmRNBdRdfpKcnGGwXNYqUNZYurB1r4Q=="],
- "@blueprintjs/select": ["@blueprintjs/select@6.1.3", "", { "dependencies": { "@blueprintjs/colors": "^5.1.14", "@blueprintjs/core": "^6.9.1", "@blueprintjs/icons": "^6.6.0", "classnames": "^2.3.1", "tslib": "~2.6.2" }, "peerDependencies": { "@types/react": "18", "react": "18", "react-dom": "18" }, "optionalPeers": ["@types/react"] }, "sha512-BsDVwHvDplhNplcPizYVPi6y0KOqAB18Nw+spaQlACKYN9A2MmRe1EOCb1xYUu2dyx3Xz1sUMr1/2l6Dy/I2Ug=="],
+ "@blueprintjs/select": ["@blueprintjs/select@6.1.4", "", { "dependencies": { "@blueprintjs/colors": "^5.1.15", "@blueprintjs/core": "^6.10.0", "@blueprintjs/icons": "^6.7.0", "classnames": "^2.3.1", "tslib": "~2.6.2" }, "peerDependencies": { "@types/react": "18", "react": "18", "react-dom": "18" }, "optionalPeers": ["@types/react"] }, "sha512-j0eAwfApM3cUjuLQALgkN7QKNKGbNlPlL4LhngENYvs9bZMuHdtARFBT9FZV3UCqiIpAQmIccdvYJ0VtwV8Irg=="],
"@csstools/color-helpers": ["@csstools/color-helpers@5.1.0", "", {}, "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA=="],
@@ -234,7 +234,7 @@
"@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="],
- "@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.8.3", "", { "dependencies": { "@types/node": ">=20.0.0", "happy-dom": "^20.8.3" } }, "sha512-9z6lLr6K6dIFLiB9jI0tKTlYzCComn5RL7L1mN3EdMSCyRQB8I6A0FO/On8yYKkXuunexzPDC98zDflvv2wDrQ=="],
+ "@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.8.4", "", { "dependencies": { "@types/node": ">=20.0.0", "happy-dom": "^20.8.4" } }, "sha512-cXGYd3xIAcviiGO6lPXdG6Yg244xwRgtY2dicAQ6HiB87E2IL2ekgfR5QIos18UtjiAsnCpLS3m78JfDorJcYg=="],
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
@@ -500,25 +500,25 @@
"@types/yargs-parser": ["@types/yargs-parser@21.0.3", "", {}, "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="],
- "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.57.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/type-utils": "8.57.0", "@typescript-eslint/utils": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.57.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ=="],
+ "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.57.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.57.1", "@typescript-eslint/type-utils": "8.57.1", "@typescript-eslint/utils": "8.57.1", "@typescript-eslint/visitor-keys": "8.57.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.57.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Gn3aqnvNl4NGc6x3/Bqk1AOn0thyTU9bqDRhiRnUWezgvr2OnhYCWCgC8zXXRVqBsIL1pSDt7T9nJUe0oM0kDQ=="],
- "@typescript-eslint/parser": ["@typescript-eslint/parser@8.57.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g=="],
+ "@typescript-eslint/parser": ["@typescript-eslint/parser@8.57.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.57.1", "@typescript-eslint/types": "8.57.1", "@typescript-eslint/typescript-estree": "8.57.1", "@typescript-eslint/visitor-keys": "8.57.1", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-k4eNDan0EIMTT/dUKc/g+rsJ6wcHYhNPdY19VoX/EOtaAG8DLtKCykhrUnuHPYvinn5jhAPgD2Qw9hXBwrahsw=="],
- "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.57.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.57.0", "@typescript-eslint/types": "^8.57.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w=="],
+ "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.57.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.57.1", "@typescript-eslint/types": "^8.57.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-vx1F37BRO1OftsYlmG9xay1TqnjNVlqALymwWVuYTdo18XuKxtBpCj1QlzNIEHlvlB27osvXFWptYiEWsVdYsg=="],
- "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.57.0", "", { "dependencies": { "@typescript-eslint/types": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0" } }, "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw=="],
+ "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.57.1", "", { "dependencies": { "@typescript-eslint/types": "8.57.1", "@typescript-eslint/visitor-keys": "8.57.1" } }, "sha512-hs/QcpCwlwT2L5S+3fT6gp0PabyGk4Q0Rv2doJXA0435/OpnSR3VRgvrp8Xdoc3UAYSg9cyUjTeFXZEPg/3OKg=="],
- "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.57.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA=="],
+ "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.57.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-0lgOZB8cl19fHO4eI46YUx2EceQqhgkPSuCGLlGi79L2jwYY1cxeYc1Nae8Aw1xjgW3PKVDLlr3YJ6Bxx8HkWg=="],
- "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.57.0", "", { "dependencies": { "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0", "@typescript-eslint/utils": "8.57.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ=="],
+ "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.57.1", "", { "dependencies": { "@typescript-eslint/types": "8.57.1", "@typescript-eslint/typescript-estree": "8.57.1", "@typescript-eslint/utils": "8.57.1", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-+Bwwm0ScukFdyoJsh2u6pp4S9ktegF98pYUU0hkphOOqdMB+1sNQhIz8y5E9+4pOioZijrkfNO/HUJVAFFfPKA=="],
- "@typescript-eslint/types": ["@typescript-eslint/types@8.57.0", "", {}, "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg=="],
+ "@typescript-eslint/types": ["@typescript-eslint/types@8.57.1", "", {}, "sha512-S29BOBPJSFUiblEl6RzPPjJt6w25A6XsBqRVDt53tA/tlL8q7ceQNZHTjPeONt/3S7KRI4quk+yP9jK2WjBiPQ=="],
- "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.57.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.57.0", "@typescript-eslint/tsconfig-utils": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q=="],
+ "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.57.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.57.1", "@typescript-eslint/tsconfig-utils": "8.57.1", "@typescript-eslint/types": "8.57.1", "@typescript-eslint/visitor-keys": "8.57.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ybe2hS9G6pXpqGtPli9Gx9quNV0TWLOmh58ADlmZe9DguLq0tiAKVjirSbtM1szG6+QH6rVXyU6GTLQbWnMY+g=="],
- "@typescript-eslint/utils": ["@typescript-eslint/utils@8.57.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ=="],
+ "@typescript-eslint/utils": ["@typescript-eslint/utils@8.57.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.57.1", "@typescript-eslint/types": "8.57.1", "@typescript-eslint/typescript-estree": "8.57.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-XUNSJ/lEVFttPMMoDVA2r2bwrl8/oPx8cURtczkSEswY5T3AeLmCy+EKWQNdL4u0MmAHOjcWrqJp2cdvgjn8dQ=="],
- "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.57.0", "", { "dependencies": { "@typescript-eslint/types": "8.57.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg=="],
+ "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.57.1", "", { "dependencies": { "@typescript-eslint/types": "8.57.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A=="],
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
@@ -1120,7 +1120,7 @@
"handlebars": ["handlebars@4.7.8", "", { "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, "optionalDependencies": { "uglify-js": "^3.1.4" }, "bin": "bin/handlebars" }, "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ=="],
- "happy-dom": ["happy-dom@20.8.3", "", { "dependencies": { "@types/node": ">=20.0.0", "@types/whatwg-mimetype": "^3.0.2", "@types/ws": "^8.18.1", "entities": "^7.0.1", "whatwg-mimetype": "^3.0.0", "ws": "^8.18.3" } }, "sha512-lMHQRRwIPyJ70HV0kkFT7jH/gXzSI7yDkQFe07E2flwmNDFoWUTRMKpW2sglsnpeA7b6S2TJPp98EbQxai8eaQ=="],
+ "happy-dom": ["happy-dom@20.8.4", "", { "dependencies": { "@types/node": ">=20.0.0", "@types/whatwg-mimetype": "^3.0.2", "@types/ws": "^8.18.1", "entities": "^7.0.1", "whatwg-mimetype": "^3.0.0", "ws": "^8.18.3" } }, "sha512-GKhjq4OQCYB4VLFBzv8mmccUadwlAusOZOI7hC1D9xDIT5HhzkJK17c4el2f6R6C715P9xB4uiMxeKUa2nHMwQ=="],
"has-ansi": ["has-ansi@2.0.0", "", { "dependencies": { "ansi-regex": "^2.0.0" } }, "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg=="],
@@ -1164,7 +1164,7 @@
"human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="],
- "i18next": ["i18next@25.8.17", "", { "dependencies": { "@babel/runtime": "^7.28.6" }, "peerDependencies": { "typescript": "^5" }, "optionalPeers": ["typescript"] }, "sha512-vWtCttyn5bpOK4hWbRAe1ZXkA+Yzcn2OcACT+WJavtfGMcxzkfvXTLMeOU8MUhRmAySKjU4VVuKlo0sSGeBokA=="],
+ "i18next": ["i18next@25.8.18", "", { "dependencies": { "@babel/runtime": "^7.28.6" }, "peerDependencies": { "typescript": "^5" }, "optionalPeers": ["typescript"] }, "sha512-lzY5X83BiL5AP77+9DydbrqkQHFN9hUzWGjqjLpPcp5ZOzuu1aSoKaU3xbBLSjWx9dAzW431y+d+aogxOZaKRA=="],
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
@@ -1788,7 +1788,7 @@
"robust-predicates": ["robust-predicates@3.0.2", "", {}, "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="],
- "rollbar": ["rollbar@3.0.0", "", { "dependencies": { "@rrweb/record": "^2.0.0-alpha.18", "async": "~3.2.3", "error-stack-parser-es": "^1.0.5", "json-stringify-safe": "~5.0.0", "lru-cache": "~2.2.1", "request-ip": "~3.3.0", "source-map": "^0.5.7" } }, "sha512-FN3dO3SQzR1u5k7fVVyC97qXtOyzM8k79cpk2crklp/NcHVPpmF0QPMypNFRN9ujoycdl77+YwGU7PAfjP/wYA=="],
+ "rollbar": ["rollbar@3.1.0", "", { "dependencies": { "@rrweb/record": "^2.0.0-alpha.18", "async": "~3.2.3", "error-stack-parser-es": "^1.0.5", "json-stringify-safe": "~5.0.0", "lru-cache": "~2.2.1", "request-ip": "~3.3.0", "source-map": "^0.5.7" } }, "sha512-YELG0MDRzOLnV5sa2vL/IiGEgNoK5JAivKlpjcJewEIqgmiVlEAPFQUNNB3fsK1PZy6VdHpsUd+Xp4qEuAylfQ=="],
"rrdom": ["rrdom@2.0.0-alpha.20", "", { "dependencies": { "rrweb-snapshot": "^2.0.0-alpha.20" } }, "sha512-hoqjS4662LtBp82qEz9GrqU36UpEmCvTA2Hns3qdF7cklLFFy3G+0Th8hLytJENleHHWxsB5nWJ3eXz5mSRxdQ=="],
diff --git a/config/initializers/mutations.rb b/config/initializers/mutations.rb
index 55736760c0..35a70b14e1 100644
--- a/config/initializers/mutations.rb
+++ b/config/initializers/mutations.rb
@@ -1,3 +1,5 @@
+require Rails.root.join("app/lib/mutations/hstore_filter")
+
MUTATIONS_DEFAULTS = Mutations::DefaultErrorMessageCreator::MESSAGES
# I don't like the errors that mutations provides for :before and :after,
diff --git a/frontend/three_d_garden/__tests__/fps_probe_test.tsx b/frontend/three_d_garden/__tests__/fps_probe_test.tsx
index fe59be6e61..5c4718ec7c 100644
--- a/frontend/three_d_garden/__tests__/fps_probe_test.tsx
+++ b/frontend/three_d_garden/__tests__/fps_probe_test.tsx
@@ -1,13 +1,14 @@
import React from "react";
import { render } from "@testing-library/react";
import * as threeFiber from "@react-three/fiber";
-import { countSceneObjects, FPSProbe } from "../fps_probe";
+import { countSceneObjects, FPSProbe, REPORT_EVERY_N } from "../fps_probe";
describe("FPSProbe", () => {
let useFrameSpy: jest.SpyInstance;
let useThreeSpy: jest.SpyInstance;
beforeEach(() => {
+ window.logStore = undefined;
useFrameSpy = jest.spyOn(threeFiber, "useFrame")
.mockImplementation(jest.fn());
useThreeSpy = jest.spyOn(threeFiber, "useThree")
@@ -92,6 +93,27 @@ describe("FPSProbe", () => {
nowSpy.mockRestore();
});
+ it("logs an fps report every nth probe", () => {
+ let t = 0;
+ const nowSpy = jest.spyOn(performance, "now").mockImplementation(() => {
+ t += 2000;
+ return t;
+ });
+ window.logStore = { log: jest.fn() };
+ render();
+ const frameHandler = useFrameSpy.mock.calls[0][0] as () => void;
+ for (let i = 0; i < 100; i++) {
+ frameHandler();
+ frameHandler();
+ }
+ expect(window.logStore.log).toHaveBeenCalledWith(
+ "3D Garden FPS",
+ { average: 1, best: 1, total: REPORT_EVERY_N, worst: 1 },
+ "info",
+ );
+ nowSpy.mockRestore();
+ });
+
it("counts scene objects", () => {
const objects = [
{ isMesh: true, type: "Mesh", name: "soil" },
diff --git a/frontend/three_d_garden/bot/components/bounds.tsx b/frontend/three_d_garden/bot/components/bounds.tsx
index f8d05ba09f..8e7aca31c7 100644
--- a/frontend/three_d_garden/bot/components/bounds.tsx
+++ b/frontend/three_d_garden/bot/components/bounds.tsx
@@ -1,15 +1,14 @@
import React from "react";
import { Config } from "../../config";
-import { Line } from "@react-three/drei";
-import { Group } from "../../components";
+import { Box, Edges } from "@react-three/drei";
+import { Group, MeshBasicMaterial } from "../../components";
import {
threeSpace,
zero as zeroFunc,
- extents as extentsFunc,
- zZero as zZeroFunc,
zDir as zDirFunc,
} from "../../helpers";
import { DistanceIndicator } from "../../elements";
+import { BackSide } from "three";
export interface BoundsProps {
config: Config;
@@ -19,33 +18,33 @@ export const Bounds = (props: BoundsProps) => {
const {
bedLengthOuter, bedWidthOuter, x, y, z,
zAxisLength, columnLength, beamLength, bounds,
- bedXOffset, bedYOffset,
+ bedXOffset, bedYOffset, botSizeX, botSizeY, botSizeZ,
} = props.config;
- const zZero = zZeroFunc(props.config);
const zDir = zDirFunc(props.config);
const zero = zeroFunc(props.config);
- const extents = extentsFunc(props.config);
- const zDip = (x: number, y: number): [number, number, number][] => [
- [x, y, extents.z],
- [x, y, zero.z],
- [x, y, extents.z],
- ];
return
-
+ position={[
+ zero.x + botSizeX / 2,
+ zero.y + botSizeY / 2,
+ zero.z - botSizeZ / 2,
+ ]}
+ args={[
+ botSizeX,
+ botSizeY,
+ botSizeZ,
+ ]}>
+
+
+
{
end={{
x: threeSpace(0, bedLengthOuter),
y: threeSpace(bedWidthOuter, bedWidthOuter),
- z: zZero - z + zAxisLength,
+ z: zero.z - z + zAxisLength,
}} />
@@ -90,12 +89,12 @@ export const Bounds = (props: BoundsProps) => {
start={{
x: threeSpace(x + 100, bedLengthOuter) + bedXOffset,
y: threeSpace(y, bedWidthOuter) + bedYOffset,
- z: zZero - zDir * z,
+ z: zero.z - zDir * z,
}}
end={{
x: threeSpace(x + 100, bedLengthOuter) + bedXOffset,
y: threeSpace(y, bedWidthOuter) + bedYOffset,
- z: zZero - zDir * z + zAxisLength,
+ z: zero.z - zDir * z + zAxisLength,
}} />
;
diff --git a/frontend/three_d_garden/fps_probe.tsx b/frontend/three_d_garden/fps_probe.tsx
index 303ecc3699..c3e9949da9 100644
--- a/frontend/three_d_garden/fps_probe.tsx
+++ b/frontend/three_d_garden/fps_probe.tsx
@@ -55,9 +55,13 @@ const formatTopCounts = (
return entries.map(([key, value]) => `${key}: ${value}`).join(", ");
};
+export const REPORT_EVERY_N = 60;
+
export const FPSProbe = () => {
const frameCount = React.useRef(0);
const lastTime = React.useRef(undefined);
+ const reportCount = React.useRef(0);
+ const samples = React.useRef([]);
const { gl, scene } = useThree();
React.useEffect(() => {
@@ -77,6 +81,7 @@ export const FPSProbe = () => {
if (now - lastTime.current >= 1000) {
const elapsed = (now - lastTime.current) / 1000;
const fps = frameCount.current / elapsed;
+ samples.current.push(fps);
const { calls, triangles, points, lines } = gl.info.render;
const { geometries, textures } = gl.info.memory;
const sceneCounts = countSceneObjects(scene as Scene);
@@ -103,6 +108,18 @@ export const FPSProbe = () => {
.map(([key, value]) => `${key}: ${value}`)
.join("\n");
console.log(linesToLog);
+ reportCount.current += 1;
+ const doReport = !(reportCount.current % REPORT_EVERY_N);
+ const average = Math.round(samples.current
+ .reduce((sum, sample) => sum + sample, 0) / samples.current.length);
+ const report = {
+ best: Math.round(Math.max(...samples.current)),
+ worst: Math.round(Math.min(...samples.current)),
+ average,
+ total: samples.current.length,
+ };
+ doReport && window.logStore?.log("3D Garden FPS", report, "info");
+ console.log(report);
frameCount.current = 0;
lastTime.current = now;
}
diff --git a/lib/tasks/api.rake b/lib/tasks/api.rake
index af2f3e5bd8..4d0cd7e430 100644
--- a/lib/tasks/api.rake
+++ b/lib/tasks/api.rake
@@ -152,12 +152,17 @@ namespace :api do
src = "node_modules/monaco-editor/min/vs"
dst = "public/assets/monaco"
lua_src = "node_modules/monaco-editor/esm/vs"
- lua = "basic-languages/lua"
- sh "mkdir -p public/assets/"
- sh "cp -r #{src} #{dst}"
- sh "rm -rf #{dst}/*language*"
- sh "mkdir #{dst}/basic-languages"
- sh "cp -r #{lua_src}/#{lua} #{dst}/#{lua}"
+ sh "mkdir -p public/assets/monaco"
+ sh "cp -r #{src}/assets #{dst}"
+ sh "cp -r #{src}/editor #{dst}"
+ sh "cp -r #{src}/basic-languages #{dst}"
+ sh "cp -r #{src}/loader.js #{dst}"
+ sh "cp -r #{src}/workers-*.js #{dst}"
+ sh "cp -r #{src}/monaco.contribution-*.js #{dst}"
+ sh "cp -r #{src}/editor.api-*.js #{dst}"
+ sh "cp -r #{src}/nls.messages-loader.js #{dst}"
+ sh "cp -r #{src}/lua-*.js #{dst}"
+ sh "cp -r #{lua_src}/basic-languages/lua #{dst}/basic-languages"
end
desc "Serve javascript assets (via Bun bundler)."
diff --git a/package.json b/package.json
index 48f88fcf5e..91f352d139 100644
--- a/package.json
+++ b/package.json
@@ -35,8 +35,8 @@
"mqtt": "mqtt/dist/mqtt.esm.js"
},
"dependencies": {
- "@blueprintjs/core": "6.9.1",
- "@blueprintjs/select": "6.1.3",
+ "@blueprintjs/core": "6.10.0",
+ "@blueprintjs/select": "6.1.4",
"@monaco-editor/react": "4.7.0",
"@react-spring/three": "10.0.3",
"@react-three/drei": "10.7.7",
@@ -61,7 +61,7 @@
"farmbot": "15.9.3",
"fengari": "0.1.5",
"fengari-web": "0.1.4",
- "i18next": "25.8.17",
+ "i18next": "25.8.18",
"lodash": "4.17.23",
"markdown-it": "14.1.1",
"markdown-it-emoji": "3.0.0",
@@ -80,7 +80,7 @@
"redux": "5.0.1",
"redux-immutable-state-invariant": "2.1.0",
"redux-thunk": "3.1.0",
- "rollbar": "3.0.0",
+ "rollbar": "3.1.0",
"suncalc": "1.9.0",
"takeme": "0.12.0",
"three": "0.183.2",
@@ -89,7 +89,7 @@
},
"devDependencies": {
"@eslint/js": "10.0.1",
- "@happy-dom/global-registrator": "20.8.3",
+ "@happy-dom/global-registrator": "20.8.4",
"@react-three/eslint-plugin": "0.1.2",
"@testing-library/dom": "10.4.1",
"@testing-library/jest-dom": "6.9.1",
@@ -99,9 +99,9 @@
"@types/jest": "30.0.0",
"@types/readable-stream": "4.0.23",
"@types/suncalc": "1.9.2",
- "@typescript-eslint/eslint-plugin": "8.57.0",
- "@typescript-eslint/parser": "8.57.0",
- "happy-dom": "20.8.3",
+ "@typescript-eslint/eslint-plugin": "8.57.1",
+ "@typescript-eslint/parser": "8.57.1",
+ "happy-dom": "20.8.4",
"eslint": "10.0.3",
"eslint-plugin-eslint-comments": "3.2.0",
"eslint-plugin-import": "2.32.0",