diff --git a/package-lock.json b/package-lock.json index 78b44785c..c73d08aa5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,21 +1,18 @@ { "name": "openscreen", - "version": "1.6.0", + "version": "1.8.0-rc.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "openscreen", - "version": "1.6.0", + "version": "1.8.0-rc.3", "dependencies": { "@fix-webm-duration/fix": "^1.0.1", "@langchain/anthropic": "^1.3.26", "@langchain/core": "^1.1.44", - "@langchain/langgraph": "^1.2.7", - "@langchain/langgraph-checkpoint": "^1.0.2", "@langchain/mistralai": "^1.0.8", "@langchain/openai": "^1.4.2", - "@pixi/filter-drop-shadow": "^5.2.0", "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", @@ -38,12 +35,9 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "deepagents": "^1.10.5", - "emoji-picker-react": "^4.18.0", - "fix-webm-duration": "^1.0.6", "gif.js": "^0.2.0", - "gsap": "^3.15.0", + "i18next": "^23.16.0", "langchain": "^1.2.39", - "langsmith": "^0.7.1", "lucide-react": "^0.545.0", "mediabunny": "^1.40.1", "motion": "^12.38.0", @@ -58,7 +52,6 @@ "sonner": "^2.0.7", "tailwind-merge": "^3.5.0", "tailwindcss-animate": "^1.0.7", - "uuid": "^13.0.0", "web-demuxer": "^4.0.0", "zod": "^4.1.12", "zustand": "^5.0.8" @@ -2363,132 +2356,12 @@ "@octokit/openapi-types": "^27.0.0" } }, - "node_modules/@pixi/color": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@pixi/color/-/color-7.4.3.tgz", - "integrity": "sha512-a6R+bXKeXMDcRmjYQoBIK+v2EYqxSX49wcjAY579EYM/WrFKS98nSees6lqVUcLKrcQh2DT9srJHX7XMny3voQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@pixi/colord": "^2.9.6" - } - }, "node_modules/@pixi/colord": { "version": "2.9.6", "resolved": "https://registry.npmjs.org/@pixi/colord/-/colord-2.9.6.tgz", "integrity": "sha512-nezytU2pw587fQstUu1AsJZDVEynjskwOL+kibwcdxsMBFqPsFFNA7xl0ii/gXuDi6M0xj3mfRJj8pBSc2jCfA==", "license": "MIT" }, - "node_modules/@pixi/constants": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@pixi/constants/-/constants-7.4.3.tgz", - "integrity": "sha512-QGmwJUNQy/vVEHzL6VGQvnwawLZ1wceZMI8HwJAT4/I2uAzbBeFDdmCS8WsTpSWLZjF/DszDc1D8BFp4pVJ5UQ==", - "license": "MIT", - "peer": true - }, - "node_modules/@pixi/core": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@pixi/core/-/core-7.4.3.tgz", - "integrity": "sha512-5YDs11faWgVVTL8VZtLU05/Fl47vaP5Tnsbf+y/WRR0VSW3KhRRGTBU1J3Gdc2xEWbJhUK07KGP7eSZpvtPVgA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@pixi/color": "7.4.3", - "@pixi/constants": "7.4.3", - "@pixi/extensions": "7.4.3", - "@pixi/math": "7.4.3", - "@pixi/runner": "7.4.3", - "@pixi/settings": "7.4.3", - "@pixi/ticker": "7.4.3", - "@pixi/utils": "7.4.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/pixijs" - } - }, - "node_modules/@pixi/extensions": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@pixi/extensions/-/extensions-7.4.3.tgz", - "integrity": "sha512-FhoiYkHQEDYHUE7wXhqfsTRz6KxLXjuMbSiAwnLb9uG1vAgp6q6qd6HEsf4X30YaZbLFY8a4KY6hFZWjF+4Fdw==", - "license": "MIT", - "peer": true - }, - "node_modules/@pixi/filter-drop-shadow": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@pixi/filter-drop-shadow/-/filter-drop-shadow-5.2.0.tgz", - "integrity": "sha512-cYS2KDER7cwCu0V4VNSxTHGvzmNcEXdC9j3031YBOkUAE3+p17LMS/TAt6XeMfJV7KaPuusvXy2NFgGkv3RDbw==", - "license": "MIT", - "dependencies": { - "@pixi/filter-kawase-blur": "5.1.1" - }, - "peerDependencies": { - "@pixi/core": "^7.0.0-X" - } - }, - "node_modules/@pixi/filter-kawase-blur": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@pixi/filter-kawase-blur/-/filter-kawase-blur-5.1.1.tgz", - "integrity": "sha512-nPnJ1ChBFP+4pgFCwC0RJgHAJCetiHcQU3INH7zCdq88cFABmVmhN+wCKRNg4H7lF1EJjaXgFDkTrTreOD/bnw==", - "license": "MIT", - "peerDependencies": { - "@pixi/core": "^7.0.0-X" - } - }, - "node_modules/@pixi/math": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@pixi/math/-/math-7.4.3.tgz", - "integrity": "sha512-/uJOVhR2DOZ+zgdI6Bs/CwcXT4bNRKsS+TqX3ekRIxPCwaLra+Qdm7aDxT5cTToDzdxbKL5+rwiLu3Y1egILDw==", - "license": "MIT", - "peer": true - }, - "node_modules/@pixi/runner": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@pixi/runner/-/runner-7.4.3.tgz", - "integrity": "sha512-TJyfp7y23u5vvRAyYhVSa7ytq0PdKSvPLXu4G3meoFh1oxTLHH6g/RIzLuxUAThPG2z7ftthuW3qWq6dRV+dhw==", - "license": "MIT", - "peer": true - }, - "node_modules/@pixi/settings": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@pixi/settings/-/settings-7.4.3.tgz", - "integrity": "sha512-SmGK8smc0PxRB9nr0UJioEtE9hl4gvj9OedCvZx3bxBwA3omA5BmP3CyhQfN8XJ29+o2OUL01r3zAPVol4l4lA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@pixi/constants": "7.4.3", - "@types/css-font-loading-module": "^0.0.12", - "ismobilejs": "^1.1.0" - } - }, - "node_modules/@pixi/ticker": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@pixi/ticker/-/ticker-7.4.3.tgz", - "integrity": "sha512-tHsAD0iOUb6QSGGw+c8cyRBvxsq/NlfzIFBZLEHhWZ+Bx4a0MmXup6I/yJDGmyPCYE+ctCcAfY13wKAzdiVFgQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@pixi/extensions": "7.4.3", - "@pixi/settings": "7.4.3", - "@pixi/utils": "7.4.3" - } - }, - "node_modules/@pixi/utils": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@pixi/utils/-/utils-7.4.3.tgz", - "integrity": "sha512-NO3Y9HAn2UKS1YdxffqsPp+kDpVm8XWvkZcS/E+rBzY9VTLnNOI7cawSRm+dacdET3a8Jad3aDKEDZ0HmAqAFA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@pixi/color": "7.4.3", - "@pixi/constants": "7.4.3", - "@pixi/settings": "7.4.3", - "@types/earcut": "^2.1.0", - "earcut": "^2.2.4", - "eventemitter3": "^4.0.0", - "url": "^0.11.0" - } - }, "node_modules/@playwright/test": { "version": "1.59.1", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", @@ -4576,13 +4449,6 @@ "assertion-error": "^2.0.1" } }, - "node_modules/@types/css-font-loading-module": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.12.tgz", - "integrity": "sha512-x2tZZYkSxXqWvTDgveSynfjq/T2HyiZHXb00j/+gy19yp70PHCizM48XFdjBCWH7eHBD0R5i/pw9yMBP/BH5uA==", - "license": "MIT", - "peer": true - }, "node_modules/@types/debug": { "version": "4.1.13", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", @@ -4615,13 +4481,6 @@ "integrity": "sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ==", "license": "MIT" }, - "node_modules/@types/earcut": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@types/earcut/-/earcut-2.1.4.tgz", - "integrity": "sha512-qp3m9PPz4gULB9MhjGID7wpo3gJ4bTGXm7ltNDsmOvsPduTeHp8wSW9YckBj3mljeOh4F0m2z/0JKAALRKbmLQ==", - "license": "MIT", - "peer": true - }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -5910,6 +5769,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -5919,23 +5779,6 @@ "node": ">= 0.4" } }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "peer": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", @@ -6715,6 +6558,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -6725,13 +6569,6 @@ "node": ">= 0.4" } }, - "node_modules/earcut": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", - "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==", - "license": "ISC", - "peer": true - }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -6961,21 +6798,6 @@ "dev": true, "license": "MIT" }, - "node_modules/emoji-picker-react": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/emoji-picker-react/-/emoji-picker-react-4.18.0.tgz", - "integrity": "sha512-vLTrLfApXAIciguGE57pXPWs9lPLBspbEpPMiUq03TIli2dHZBiB+aZ0R9/Wat0xmTfcd4AuEzQgSYxEZ8C88Q==", - "license": "MIT", - "dependencies": { - "flairup": "1.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "react": ">=16" - } - }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -7040,6 +6862,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -7065,6 +6888,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -7396,18 +7220,6 @@ "node": ">=8" } }, - "node_modules/fix-webm-duration": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/fix-webm-duration/-/fix-webm-duration-1.0.6.tgz", - "integrity": "sha512-zVAqi4gE+8ywxJuAyV/rlJVX6CMtvyapEbQx6jyoeX9TMjdqAlt/FdG5d7rXSSkDVzTvS0H7CtwzHcH/vh4FPA==", - "license": "MIT" - }, - "node_modules/flairup": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/flairup/-/flairup-1.0.0.tgz", - "integrity": "sha512-IKlE+pNvL2R+kVL1kEhUYqRxVqeFnjiIvHWDMLFXNaqyUdFXQM2wte44EfMYJNHkW16X991t2Zg8apKkhv7OBA==", - "license": "MIT" - }, "node_modules/form-data": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", @@ -7548,6 +7360,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -7581,6 +7394,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -7741,6 +7555,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -7782,12 +7597,6 @@ "dev": true, "license": "ISC" }, - "node_modules/gsap": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.15.0.tgz", - "integrity": "sha512-dMW4CWBTUK1AEEDeZc1g4xpPGIrSf9fJF960qbTZmN/QwZIWY5wgliS6JWl9/25fpTGJrMRtSjGtOmPnfjZB+A==", - "license": "Standard 'no charge' license: https://gsap.com/standard-license." - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -7816,6 +7625,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -7963,6 +7773,29 @@ "url": "https://github.com/sponsors/typicode" } }, + "node_modules/i18next": { + "version": "23.16.8", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.8.tgz", + "integrity": "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, "node_modules/iconv-corefoundation": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", @@ -8735,6 +8568,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -9229,19 +9063,6 @@ "node": ">= 6" } }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -10083,22 +9904,6 @@ ], "license": "MIT" }, - "node_modules/qs": { - "version": "6.15.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", - "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -10719,82 +10524,6 @@ "node": ">=8" } }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "peer": true, - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "license": "MIT", - "peer": true, - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "peer": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", @@ -11620,27 +11349,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", - "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", - "license": "MIT", - "peer": true, - "dependencies": { - "punycode": "^1.4.1", - "qs": "^6.12.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/url/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "license": "MIT", - "peer": true - }, "node_modules/use-callback-ref": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", @@ -11706,19 +11414,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, - "node_modules/uuid": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", - "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist-node/bin/uuid" - } - }, "node_modules/verror": { "version": "1.10.1", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", diff --git a/package.json b/package.json index b818ffc4c..46210df76 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "clsx": "^2.1.1", "deepagents": "^1.10.5", "gif.js": "^0.2.0", + "i18next": "^23.16.0", "langchain": "^1.2.39", "lucide-react": "^0.545.0", "mediabunny": "^1.40.1", diff --git a/src/i18n/loader.ts b/src/i18n/loader.ts index 36d8eb651..e5ebbb560 100644 --- a/src/i18n/loader.ts +++ b/src/i18n/loader.ts @@ -1,111 +1,72 @@ +import i18next, { type Resource } from "i18next"; import { DEFAULT_LOCALE, I18N_NAMESPACES, type I18nNamespace, type Locale } from "./config"; -type MessageMap = Record; -type LocaleValidationError = { - locale: string; - missingNamespaces: I18nNamespace[]; -}; - -const modules = import.meta.glob("./locales/**/*.json", { eager: true }) as Record< - string, - { default: MessageMap } ->; - -const messages: Record> = {}; +const modules = import.meta.glob("./locales/**/*.json", { + eager: true, + import: "default", +}) as Record; +const byLocale: Record> = {}; for (const [path, mod] of Object.entries(modules)) { - // path looks like "./locales/en/common.json" - const parts = path.replace("./locales/", "").replace(".json", "").split("/"); - const locale = parts[0]; - const namespace = parts[1]; - if (!messages[locale]) messages[locale] = {}; - messages[locale][namespace] = mod.default; + const [locale, namespace] = path.replace("./locales/", "").replace(".json", "").split("/"); + if (!byLocale[locale]) byLocale[locale] = {}; + byLocale[locale][namespace] = mod; } -const REQUIRED_NAMESPACES = new Set(I18N_NAMESPACES); - -const localeValidationErrors: LocaleValidationError[] = Object.keys(messages) - .map((locale) => { - const localeMessages = messages[locale] ?? {}; - const missingNamespaces = I18N_NAMESPACES.filter((namespace) => !localeMessages[namespace]); - return { - locale, - missingNamespaces, - }; - }) - .filter((entry) => entry.missingNamespaces.length > 0); - -const invalidLocales = new Set(localeValidationErrors.map((entry) => entry.locale)); - -const availableLocales = Object.keys(messages) - .filter((locale) => REQUIRED_NAMESPACES.size > 0 && hasRequiredNamespaces(messages[locale])) - .filter((locale) => !invalidLocales.has(locale)) - .sort((a, b) => { - if (a === DEFAULT_LOCALE) return -1; - if (b === DEFAULT_LOCALE) return 1; - return a.localeCompare(b); - }); +const isComplete = (locale: string) => I18N_NAMESPACES.every((ns) => byLocale[locale]?.[ns]); -if (localeValidationErrors.length > 0) { - console.error("[i18n] Incomplete locale folders were excluded:"); - for (const entry of localeValidationErrors) { - console.error( - `[i18n] ${entry.locale}: missing ${entry.missingNamespaces.map((ns) => `${ns}.json`).join(", ")}`, - ); - } +const incomplete = Object.keys(byLocale).filter((locale) => !isComplete(locale)); +if (incomplete.length > 0) { + console.error("[i18n] Incomplete locale folders were excluded:", incomplete.join(", ")); } -function hasRequiredNamespaces(localeMessages: Record | undefined): boolean { - if (!localeMessages) return false; - for (const namespace of REQUIRED_NAMESPACES) { - if (!localeMessages[namespace]) return false; - } - return true; -} - -function isAvailableLocale(locale: string): locale is Locale { - return availableLocales.includes(locale); +const availableLocales = Object.keys(byLocale) + .filter(isComplete) + .sort((a, b) => (a === DEFAULT_LOCALE ? -1 : b === DEFAULT_LOCALE ? 1 : a.localeCompare(b))); + +const resources = Object.fromEntries( + availableLocales.map((locale) => [locale, byLocale[locale] as Resource[string]]), +) as Resource; + +// `lng` is inert here: every lookup below passes an explicit `lng`, so the init +// language never reaches the output. Reading localStorage at module scope would +// add a failure mode for no gain — it can throw SecurityError, and at module +// scope that takes down the whole graph rather than one component. The user's +// stored preference is applied by I18nContext, which guards its access. +await i18next.init({ + lng: DEFAULT_LOCALE, + fallbackLng: DEFAULT_LOCALE, + defaultNS: "common", + resources, + interpolation: { escapeValue: false }, + // A non-leaf key would otherwise render i18next's English developer message + // ("key 'x' returned an object instead of string") straight into the UI. + // Returning undefined keeps the old loader's behaviour: fall through to the + // `namespace.key` marker. + returnedObjectHandler: () => undefined, +}); + +function tAt( + lng: Locale, + ns: I18nNamespace, + key: string, + vars?: Record, +): string | undefined { + if (!i18next.exists(key, { lng, ns })) return undefined; + const result = i18next.t(key, { lng, ns, ...vars }); + return typeof result === "string" ? result : undefined; } export function getAvailableLocales(): Locale[] { - if (availableLocales.length === 0) { - return [DEFAULT_LOCALE]; - } - return availableLocales; -} - -export function getLocaleValidationErrors(): LocaleValidationError[] { - return localeValidationErrors; -} - -function getMessageValue(obj: unknown, dotPath: string): string | undefined { - const keys = dotPath.split("."); - let current: unknown = obj; - for (const key of keys) { - if (current == null || typeof current !== "object") return undefined; - current = (current as Record)[key]; - } - return typeof current === "string" ? current : undefined; -} - -function interpolate(str: string, vars?: Record): string { - if (!vars) return str; - return str.replace(/\{\{(\w+)\}\}/g, (_, key: string) => String(vars[key] ?? `{{${key}}}`)); -} - -export function getMessages(locale: Locale, namespace: I18nNamespace): MessageMap { - const resolvedLocale = isAvailableLocale(locale) ? locale : DEFAULT_LOCALE; - return messages[resolvedLocale]?.[namespace] ?? {}; + return availableLocales.length > 0 ? availableLocales : [DEFAULT_LOCALE]; } export function getLocaleName(locale: Locale): string { - const resolvedLocale = isAvailableLocale(locale) ? locale : DEFAULT_LOCALE; - return getMessageValue(messages[resolvedLocale]?.common, "locale.name") ?? locale; + return tAt(locale, "common", "locale.name") ?? locale; } export function getLocaleShort(locale: Locale): string { - const resolvedLocale = isAvailableLocale(locale) ? locale : DEFAULT_LOCALE; - return getMessageValue(messages[resolvedLocale]?.common, "locale.short") ?? locale; + return tAt(locale, "common", "locale.short") ?? locale; } export function translate( @@ -114,12 +75,5 @@ export function translate( key: string, vars?: Record, ): string { - const value = - getMessageValue( - messages[isAvailableLocale(locale) ? locale : DEFAULT_LOCALE]?.[namespace], - key, - ) ?? getMessageValue(messages[DEFAULT_LOCALE]?.[namespace], key); - - if (value == null) return `${namespace}.${key}`; - return interpolate(value, vars); + return tAt(locale, namespace, key, vars) ?? `${namespace}.${key}`; }