diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 312163d4f..de252c393 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -108,6 +108,13 @@ "vitest": "^3.2.4" } }, + "node_modules/@acemir/cssom": { + "version": "0.9.22", + "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.22.tgz", + "integrity": "sha512-QviHW7uL3M3oQ5b5z+6AqDe+ZzJ3XeLLKNaD+XbuRIMkeAZ/FsL7zIle0V+YR5bllZDL4s1i+NYx8wNGNpnQTg==", + "dev": true, + "license": "MIT" + }, "node_modules/@adobe/css-tools": { "version": "4.4.4", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", @@ -180,9 +187,9 @@ } }, "node_modules/@asamuzakjp/dom-selector": { - "version": "6.7.3", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.7.3.tgz", - "integrity": "sha512-kiGFeY+Hxf5KbPpjRLf+ffWbkos1aGo8MBfd91oxS3O57RgU3XhZrt/6UzoVF9VMpWbC3v87SRc9jxGrc9qHtQ==", + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.7.4.tgz", + "integrity": "sha512-buQDjkm+wDPXd6c13534URWZqbz0RP5PAhXZ+LIoa5LgwInT9HVJvGIJivg75vi8I13CxDGdTnz+aY5YUJlIAA==", "dev": true, "license": "MIT", "dependencies": { @@ -456,9 +463,9 @@ } }, "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.14.tgz", - "integrity": "sha512-zSlIxa20WvMojjpCSy8WrNpcZ61RqfTfX3XTaOeVlGJrt/8HF3YbzgFZa01yTbT4GWQLwfTcC3EB8i3XnB647Q==", + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.15.tgz", + "integrity": "sha512-q0p6zkVq2lJnmzZVPR33doA51G7YOja+FBvRdp5ISIthL0MtFCgYHHhR563z9WFGxcOn0WfjSkPDJ5Qig3H3Sw==", "dev": true, "funding": [ { @@ -473,9 +480,6 @@ "license": "MIT-0", "engines": { "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" } }, "node_modules/@csstools/css-tokenizer": { @@ -933,9 +937,9 @@ } }, "node_modules/@emnapi/core": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.6.0.tgz", - "integrity": "sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.0.tgz", + "integrity": "sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw==", "license": "MIT", "optional": true, "dependencies": { @@ -951,9 +955,9 @@ "optional": true }, "node_modules/@emnapi/runtime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.6.0.tgz", - "integrity": "sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.0.tgz", + "integrity": "sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==", "license": "MIT", "optional": true, "dependencies": { @@ -1131,9 +1135,9 @@ "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", - "integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", "cpu": [ "ppc64" ], @@ -1148,9 +1152,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz", - "integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", "cpu": [ "arm" ], @@ -1165,9 +1169,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz", - "integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", "cpu": [ "arm64" ], @@ -1182,9 +1186,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz", - "integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", "cpu": [ "x64" ], @@ -1199,9 +1203,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz", - "integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", "cpu": [ "arm64" ], @@ -1216,9 +1220,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz", - "integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", "cpu": [ "x64" ], @@ -1233,9 +1237,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz", - "integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", "cpu": [ "arm64" ], @@ -1250,9 +1254,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz", - "integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", "cpu": [ "x64" ], @@ -1267,9 +1271,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz", - "integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", "cpu": [ "arm" ], @@ -1284,9 +1288,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz", - "integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", "cpu": [ "arm64" ], @@ -1301,9 +1305,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz", - "integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", "cpu": [ "ia32" ], @@ -1318,9 +1322,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz", - "integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", "cpu": [ "loong64" ], @@ -1335,9 +1339,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz", - "integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", "cpu": [ "mips64el" ], @@ -1352,9 +1356,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz", - "integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", "cpu": [ "ppc64" ], @@ -1369,9 +1373,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz", - "integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", "cpu": [ "riscv64" ], @@ -1386,9 +1390,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz", - "integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", "cpu": [ "s390x" ], @@ -1403,9 +1407,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz", - "integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", "cpu": [ "x64" ], @@ -1420,9 +1424,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz", - "integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", "cpu": [ "arm64" ], @@ -1437,9 +1441,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz", - "integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", "cpu": [ "x64" ], @@ -1454,9 +1458,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz", - "integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", "cpu": [ "arm64" ], @@ -1471,9 +1475,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz", - "integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", "cpu": [ "x64" ], @@ -1488,9 +1492,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz", - "integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", "cpu": [ "arm64" ], @@ -1505,9 +1509,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz", - "integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", "cpu": [ "x64" ], @@ -1522,9 +1526,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz", - "integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", "cpu": [ "arm64" ], @@ -1539,9 +1543,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz", - "integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", "cpu": [ "ia32" ], @@ -1556,9 +1560,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz", - "integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", "cpu": [ "x64" ], @@ -1641,22 +1645,22 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.1.tgz", - "integrity": "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.16.0" + "@eslint/core": "^0.17.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz", - "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1738,9 +1742,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.38.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.38.0.tgz", - "integrity": "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", + "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", "dev": true, "license": "MIT", "engines": { @@ -1761,13 +1765,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz", - "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.16.0", + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { @@ -1880,9 +1884,9 @@ } }, "node_modules/@iconify-json/material-symbols": { - "version": "1.2.43", - "resolved": "https://registry.npmjs.org/@iconify-json/material-symbols/-/material-symbols-1.2.43.tgz", - "integrity": "sha512-Atn0KzZkk96vZuSA//BEzwd9we8FUbifNIWxS9+ngPbWsZEKv1WAe+Gxle8BQjvnfIpewCP1opIeHKMxcNEmPw==", + "version": "1.2.44", + "resolved": "https://registry.npmjs.org/@iconify-json/material-symbols/-/material-symbols-1.2.44.tgz", + "integrity": "sha512-NAJjhswaK9FxBeIzFFsNygws7wHtmAkBWhF4YEwn1NZIMbA+LNITqhUiq6sP5mOdKQqnoritFTlQaZ47a5BgBg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2111,9 +2115,9 @@ "license": "MIT" }, "node_modules/@mantine/core": { - "version": "8.3.5", - "resolved": "https://registry.npmjs.org/@mantine/core/-/core-8.3.5.tgz", - "integrity": "sha512-PdVNLMgOS2vFhOujRi6/VC9ic8w3UDyKX7ftwDeJ7yQT8CiepUxfbWWYpVpnq23bdWh/7fIT2Pn1EY8r8GOk7g==", + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/@mantine/core/-/core-8.3.6.tgz", + "integrity": "sha512-paTl+0x+O/QtgMtqVJaG8maD8sfiOdgPmLOyG485FmeGZ1L3KMdEkhxZtmdGlDFsLXhmMGQ57ducT90bvhXX5A==", "license": "MIT", "dependencies": { "@floating-ui/react": "^0.27.16", @@ -2124,46 +2128,46 @@ "type-fest": "^4.41.0" }, "peerDependencies": { - "@mantine/hooks": "8.3.5", + "@mantine/hooks": "8.3.6", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "node_modules/@mantine/dates": { - "version": "8.3.5", - "resolved": "https://registry.npmjs.org/@mantine/dates/-/dates-8.3.5.tgz", - "integrity": "sha512-LkIdC4eWPNQFv1BU1c52U3Z3RuA3yU1asvTgMEIQ/MdJsGK8GePwpgMH/jKQ8ba/AW9NfksdvtOJ6uIqPwjCkg==", + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/@mantine/dates/-/dates-8.3.6.tgz", + "integrity": "sha512-lSi1zvyL86SKeePH0J3vOjAR7ZIVNOrZm6ja7jAH6IBdcpQOKH8TXbrcAi5okEStvmvkne7pVaGu0VkdE8KnAw==", "license": "MIT", "dependencies": { "clsx": "^2.1.1" }, "peerDependencies": { - "@mantine/core": "8.3.5", - "@mantine/hooks": "8.3.5", + "@mantine/core": "8.3.6", + "@mantine/hooks": "8.3.6", "dayjs": ">=1.0.0", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "node_modules/@mantine/dropzone": { - "version": "8.3.5", - "resolved": "https://registry.npmjs.org/@mantine/dropzone/-/dropzone-8.3.5.tgz", - "integrity": "sha512-8eRNoEYQzUOav8Te58osGRt2vbqJO3ZORXgKALY+FhB0YGRCZYmS/gQ2T66SpyrpovY6k6OVi1ScM3wRaJrxUg==", + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/@mantine/dropzone/-/dropzone-8.3.6.tgz", + "integrity": "sha512-zgHEoO4z4hjDMMuVapwGoMahkp3lZvaht1bYc0e1hUMVN3FQNRWBrmczL9a3CG21a1cbUdwr8cbHIxOBFVAR+Q==", "license": "MIT", "dependencies": { "react-dropzone": "14.3.8" }, "peerDependencies": { - "@mantine/core": "8.3.5", - "@mantine/hooks": "8.3.5", + "@mantine/core": "8.3.6", + "@mantine/hooks": "8.3.6", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "node_modules/@mantine/hooks": { - "version": "8.3.5", - "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-8.3.5.tgz", - "integrity": "sha512-0Wf08eWLKi3WkKlxnV1W5vfuN6wcvAV2VbhQlOy0R9nrWorGTtonQF6qqBE3PnJFYF1/ZE+HkYZQ/Dr7DmYSMQ==", + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-8.3.6.tgz", + "integrity": "sha512-liHfaWXHAkLjJy+Bkr29UsCwAoDQ/a64WrM67lksx8F0qqyjR5RQH8zVlhuOjdpQnwtlUkE/YiTvbJiPcoI0bw==", "license": "MIT", "peerDependencies": { "react": "^18.x || ^19.x" @@ -2181,9 +2185,9 @@ } }, "node_modules/@maxim_mazurok/gapi.client.drive-v3": { - "version": "0.1.20251021", - "resolved": "https://registry.npmjs.org/@maxim_mazurok/gapi.client.drive-v3/-/gapi.client.drive-v3-0.1.20251021.tgz", - "integrity": "sha512-bfjWWFpDm+Y5PcssNb2JzrxgtmEKfva+HTngPmsHinBH9JC7qe9OuODUtWcwKyUJE1d0DendLjOhD9m7GDLX2A==", + "version": "0.1.20251104", + "resolved": "https://registry.npmjs.org/@maxim_mazurok/gapi.client.drive-v3/-/gapi.client.drive-v3-0.1.20251104.tgz", + "integrity": "sha512-Z2D5Ce5Q32ev2deqmb7RgPp8c+AqrTylRw8iONLlb/iN/jgtGoKTUjgumlD8Vo+etJxcOWVjze+d00I3mggc/g==", "dev": true, "license": "MIT", "dependencies": { @@ -2192,9 +2196,9 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.4.tgz", - "integrity": "sha512-BIktMapG3r4iXwIhYNpvk97ZfYWTreBBQTWjQKbNbzI64+ULHfYavQEX2w99aSWHS58DvXESWIgbD9adKcUOBw==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.5.tgz", + "integrity": "sha512-kOLwlcDPnVz2QMhiBv0OQ8le8hTCqKM9cRXlfVPL91l3RGeOsxrIhNRsUt3Xb8wb+pTVUolW+JXKym93vRKxCw==", "license": "MIT", "funding": { "type": "opencollective", @@ -2202,9 +2206,9 @@ } }, "node_modules/@mui/icons-material": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.3.4.tgz", - "integrity": "sha512-9n6Xcq7molXWYb680N2Qx+FRW8oT6j/LXF5PZFH3ph9X/Rct0B/BlLAsFI7iL9ySI6LVLuQIVtrLiPT82R7OZw==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.3.5.tgz", + "integrity": "sha512-LciL1GLMZ+VlzyHAALSVAR22t8IST4LCXmljcUSx2NOutgO2XnxdIp8ilFbeNf9wpo0iUFbAuoQcB7h+HHIf3A==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.28.4" @@ -2217,7 +2221,7 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^7.3.4", + "@mui/material": "^7.3.5", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -2228,22 +2232,22 @@ } }, "node_modules/@mui/material": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.4.tgz", - "integrity": "sha512-gEQL9pbJZZHT7lYJBKQCS723v1MGys2IFc94COXbUIyCTWa+qC77a7hUax4Yjd5ggEm35dk4AyYABpKKWC4MLw==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.5.tgz", + "integrity": "sha512-8VVxFmp1GIm9PpmnQoCoYo0UWHoOrdA57tDL62vkpzEgvb/d71Wsbv4FRg7r1Gyx7PuSo0tflH34cdl/NvfHNQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.28.4", - "@mui/core-downloads-tracker": "^7.3.4", - "@mui/system": "^7.3.3", - "@mui/types": "^7.4.7", - "@mui/utils": "^7.3.3", + "@mui/core-downloads-tracker": "^7.3.5", + "@mui/system": "^7.3.5", + "@mui/types": "^7.4.8", + "@mui/utils": "^7.3.5", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.12", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1", - "react-is": "^19.1.1", + "react-is": "^19.2.0", "react-transition-group": "^4.4.5" }, "engines": { @@ -2256,7 +2260,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^7.3.3", + "@mui/material-pigment-css": "^7.3.5", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -2277,13 +2281,13 @@ } }, "node_modules/@mui/private-theming": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.3.tgz", - "integrity": "sha512-OJM+9nj5JIyPUvsZ5ZjaeC9PfktmK+W5YaVLToLR8L0lB/DGmv1gcKE43ssNLSvpoW71Hct0necfade6+kW3zQ==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.5.tgz", + "integrity": "sha512-cTx584W2qrLonwhZLbEN7P5pAUu0nZblg8cLBlTrZQ4sIiw8Fbvg7GvuphQaSHxPxrCpa7FDwJKtXdbl2TSmrA==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.28.4", - "@mui/utils": "^7.3.3", + "@mui/utils": "^7.3.5", "prop-types": "^15.8.1" }, "engines": { @@ -2304,9 +2308,9 @@ } }, "node_modules/@mui/styled-engine": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.3.tgz", - "integrity": "sha512-CmFxvRJIBCEaWdilhXMw/5wFJ1+FT9f3xt+m2pPXhHPeVIbBg9MnMvNSJjdALvnQJMPw8jLhrUtXmN7QAZV2fw==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.5.tgz", + "integrity": "sha512-zbsZ0uYYPndFCCPp2+V3RLcAN6+fv4C8pdwRx6OS3BwDkRCN8WBehqks7hWyF3vj1kdQLIWrpdv/5Y0jHRxYXQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.28.4", @@ -2338,16 +2342,16 @@ } }, "node_modules/@mui/system": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.3.tgz", - "integrity": "sha512-Lqq3emZr5IzRLKaHPuMaLBDVaGvxoh6z7HMWd1RPKawBM5uMRaQ4ImsmmgXWtwJdfZux5eugfDhXJUo2mliS8Q==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.5.tgz", + "integrity": "sha512-yPaf5+gY3v80HNkJcPi6WT+r9ebeM4eJzrREXPxMt7pNTV/1eahyODO4fbH3Qvd8irNxDFYn5RQ3idHW55rA6g==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.28.4", - "@mui/private-theming": "^7.3.3", - "@mui/styled-engine": "^7.3.3", - "@mui/types": "^7.4.7", - "@mui/utils": "^7.3.3", + "@mui/private-theming": "^7.3.5", + "@mui/styled-engine": "^7.3.5", + "@mui/types": "^7.4.8", + "@mui/utils": "^7.3.5", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -2378,9 +2382,9 @@ } }, "node_modules/@mui/types": { - "version": "7.4.7", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.7.tgz", - "integrity": "sha512-8vVje9rdEr1rY8oIkYgP+Su5Kwl6ik7O3jQ0wl78JGSmiZhRHV+vkjooGdKD8pbtZbutXFVTWQYshu2b3sG9zw==", + "version": "7.4.8", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.8.tgz", + "integrity": "sha512-ZNXLBjkPV6ftLCmmRCafak3XmSn8YV0tKE/ZOhzKys7TZXUiE0mZxlH8zKDo6j6TTUaDnuij68gIG+0Ucm7Xhw==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.28.4" @@ -2395,17 +2399,17 @@ } }, "node_modules/@mui/utils": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.3.tgz", - "integrity": "sha512-kwNAUh7bLZ7mRz9JZ+6qfRnnxbE4Zuc+RzXnhSpRSxjTlSTj7b4JxRLXpG+MVtPVtqks5k/XC8No1Vs3x4Z2gg==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.5.tgz", + "integrity": "sha512-jisvFsEC3sgjUjcPnR4mYfhzjCDIudttSGSbe1o/IXFNu0kZuR+7vqQI0jg8qtcVZBHWrwTfvAZj9MNMumcq1g==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.28.4", - "@mui/types": "^7.4.7", + "@mui/types": "^7.4.8", "@types/prop-types": "^15.7.15", "clsx": "^2.1.1", "prop-types": "^15.8.1", - "react-is": "^19.1.1" + "react-is": "^19.2.0" }, "engines": { "node": ">=14.0.0" @@ -2715,15 +2719,18 @@ } }, "node_modules/@posthog/core": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.4.0.tgz", - "integrity": "sha512-jmW8/I//YOHAfjzokqas+Qtc2T57Ux8d2uIJu7FLcMGxywckHsl6od59CD18jtUzKToQdjQhV6Y3429qj+KeNw==", - "license": "MIT" + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.5.2.tgz", + "integrity": "sha512-iedUP3EnOPPxTA2VaIrsrd29lSZnUV+ZrMnvY56timRVeZAXoYCkmjfIs3KBAsF8OUT5h1GXLSkoQdrV0r31OQ==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.6" + } }, "node_modules/@puppeteer/browsers": { - "version": "2.10.12", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.12.tgz", - "integrity": "sha512-mP9iLFZwH+FapKJLeA7/fLqOlSUwYpMwjR1P5J23qd4e7qGJwecJccJqHYrjw33jmIZYV4dtiTHPD/J+1e7cEw==", + "version": "2.10.13", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.13.tgz", + "integrity": "sha512-a9Ruw3j3qlnB5a/zHRTkruppynxqaeE4H9WNj5eYGRWqw0ZauZ23f4W2ARf3hghF5doozyD+CRtt7XSYuYRI/Q==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2794,9 +2801,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.43", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.43.tgz", - "integrity": "sha512-5Uxg7fQUCmfhax7FJke2+8B6cqgeUJUD9o2uXIKXhD+mG0mL6NObmVoi9wXEU1tY89mZKgAYA6fTbftx3q2ZPQ==", + "version": "1.0.0-beta.46", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.46.tgz", + "integrity": "sha512-xMNwJo/pHkEP/mhNVnW+zUiJDle6/hxrwO0mfSJuEVRbBfgrJFuUSRoZx/nYUw5pCjrysl9OkNXCkAdih8GCnA==", "dev": true, "license": "MIT" }, @@ -3131,9 +3138,9 @@ "license": "MIT" }, "node_modules/@sindresorhus/is": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.1.0.tgz", - "integrity": "sha512-7F/yz2IphV39hiS2zB4QYVkivrptHHh0K8qJJd9HhuWSdvf8AN7NpebW3CcDZDBQsUPMoDKWsY2WWgW7bqOcfA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.1.1.tgz", + "integrity": "sha512-rO92VvpgMc3kfiTjGT52LEtJ8Yc5kCWhZjLQ3LwlA4pSgPpQO7bVpYXParOD8Jwf+cVQECJo3yP/4I8aZtUQTQ==", "license": "MIT", "engines": { "node": ">=18" @@ -3285,15 +3292,15 @@ } }, "node_modules/@swc/core": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.5.tgz", - "integrity": "sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.0.tgz", + "integrity": "sha512-8SnJV+JV0rYbfSiEiUvYOmf62E7QwsEG+aZueqSlKoxFt0pw333+bgZSQXGUV6etXU88nxur0afVMaINujBMSw==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.24" + "@swc/types": "^0.1.25" }, "engines": { "node": ">=10" @@ -3303,16 +3310,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.13.5", - "@swc/core-darwin-x64": "1.13.5", - "@swc/core-linux-arm-gnueabihf": "1.13.5", - "@swc/core-linux-arm64-gnu": "1.13.5", - "@swc/core-linux-arm64-musl": "1.13.5", - "@swc/core-linux-x64-gnu": "1.13.5", - "@swc/core-linux-x64-musl": "1.13.5", - "@swc/core-win32-arm64-msvc": "1.13.5", - "@swc/core-win32-ia32-msvc": "1.13.5", - "@swc/core-win32-x64-msvc": "1.13.5" + "@swc/core-darwin-arm64": "1.15.0", + "@swc/core-darwin-x64": "1.15.0", + "@swc/core-linux-arm-gnueabihf": "1.15.0", + "@swc/core-linux-arm64-gnu": "1.15.0", + "@swc/core-linux-arm64-musl": "1.15.0", + "@swc/core-linux-x64-gnu": "1.15.0", + "@swc/core-linux-x64-musl": "1.15.0", + "@swc/core-win32-arm64-msvc": "1.15.0", + "@swc/core-win32-ia32-msvc": "1.15.0", + "@swc/core-win32-x64-msvc": "1.15.0" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" @@ -3324,9 +3331,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.5.tgz", - "integrity": "sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.0.tgz", + "integrity": "sha512-TBKWkbnShnEjlIbO4/gfsrIgAqHBVqgPWLbWmPdZ80bF393yJcLgkrb7bZEnJs6FCbSSuGwZv2rx1jDR2zo6YA==", "cpu": [ "arm64" ], @@ -3341,9 +3348,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.5.tgz", - "integrity": "sha512-ILd38Fg/w23vHb0yVjlWvQBoE37ZJTdlLHa8LRCFDdX4WKfnVBiblsCU9ar4QTMNdeTBEX9iUF4IrbNWhaF1Ng==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.0.tgz", + "integrity": "sha512-f5JKL1v1H56CIZc1pVn4RGPOfnWqPwmuHdpf4wesvXunF1Bx85YgcspW5YxwqG5J9g3nPU610UFuExJXVUzOiQ==", "cpu": [ "x64" ], @@ -3358,9 +3365,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.5.tgz", - "integrity": "sha512-Q6eS3Pt8GLkXxqz9TAw+AUk9HpVJt8Uzm54MvPsqp2yuGmY0/sNaPPNVqctCX9fu/Nu8eaWUen0si6iEiCsazQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.0.tgz", + "integrity": "sha512-duK6nG+WyuunnfsfiTUQdzC9Fk8cyDLqT9zyXvY2i2YgDu5+BH5W6wM5O4mDNCU5MocyB/SuF5YDF7XySnowiQ==", "cpu": [ "arm" ], @@ -3375,9 +3382,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.5.tgz", - "integrity": "sha512-aNDfeN+9af+y+M2MYfxCzCy/VDq7Z5YIbMqRI739o8Ganz6ST+27kjQFd8Y/57JN/hcnUEa9xqdS3XY7WaVtSw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.0.tgz", + "integrity": "sha512-ITe9iDtTRXM98B91rvyPP6qDVbhUBnmA/j4UxrHlMQ0RlwpqTjfZYZkD0uclOxSZ6qIrOj/X5CaoJlDUuQ0+Cw==", "cpu": [ "arm64" ], @@ -3392,9 +3399,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.5.tgz", - "integrity": "sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.0.tgz", + "integrity": "sha512-Q5ldc2bzriuzYEoAuqJ9Vr3FyZhakk5hiwDbniZ8tlEXpbjBhbOleGf9/gkhLaouDnkNUEazFW9mtqwUTRdh7Q==", "cpu": [ "arm64" ], @@ -3409,9 +3416,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.5.tgz", - "integrity": "sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.0.tgz", + "integrity": "sha512-pY4is+jEpOxlYCSnI+7N8Oxbap9TmTz5YT84tUvRTlOlTBwFAUlWFCX0FRwWJlsfP0TxbqhIe8dNNzlsEmJbXQ==", "cpu": [ "x64" ], @@ -3426,9 +3433,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.5.tgz", - "integrity": "sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.0.tgz", + "integrity": "sha512-zYEt5eT8y8RUpoe7t5pjpoOdGu+/gSTExj8PV86efhj6ugB3bPlj3Y85ogdW3WMVXr4NvwqvzdaYGCZfXzSyVg==", "cpu": [ "x64" ], @@ -3443,9 +3450,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.5.tgz", - "integrity": "sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.0.tgz", + "integrity": "sha512-zC1rmOgFH5v2BCbByOazEqs0aRNpTdLRchDExfcCfgKgeaD+IdpUOqp7i3VG1YzkcnbuZjMlXfM0ugpt+CddoA==", "cpu": [ "arm64" ], @@ -3460,9 +3467,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.5.tgz", - "integrity": "sha512-C5Yi/xIikrFUzZcyGj9L3RpKljFvKiDMtyDzPKzlsDrKIw2EYY+bF88gB6oGY5RGmv4DAX8dbnpRAqgFD0FMEw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.0.tgz", + "integrity": "sha512-7t9U9KwMwQblkdJIH+zX1V4q1o3o41i0HNO+VlnAHT5o+5qHJ963PHKJ/pX3P2UlZnBCY465orJuflAN4rAP9A==", "cpu": [ "ia32" ], @@ -3477,9 +3484,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.5.tgz", - "integrity": "sha512-YrKdMVxbYmlfybCSbRtrilc6UA8GF5aPmGKBdPvjrarvsmf4i7ZHGCEnLtfOMd3Lwbs2WUZq3WdMbozYeLU93Q==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.0.tgz", + "integrity": "sha512-VE0Zod5vcs8iMLT64m5QS1DlTMXJFI/qSgtMDRx8rtZrnjt6/9NW8XUaiPJuRu8GluEO1hmHoyf1qlbY19gGSQ==", "cpu": [ "x64" ], @@ -3523,47 +3530,47 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.16.tgz", - "integrity": "sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz", + "integrity": "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==", "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", - "magic-string": "^0.30.19", + "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.1.16" + "tailwindcss": "4.1.17" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.16.tgz", - "integrity": "sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.17.tgz", + "integrity": "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==", "license": "MIT", "engines": { "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.16", - "@tailwindcss/oxide-darwin-arm64": "4.1.16", - "@tailwindcss/oxide-darwin-x64": "4.1.16", - "@tailwindcss/oxide-freebsd-x64": "4.1.16", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.16", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.16", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.16", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.16", - "@tailwindcss/oxide-linux-x64-musl": "4.1.16", - "@tailwindcss/oxide-wasm32-wasi": "4.1.16", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.16", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.16" + "@tailwindcss/oxide-android-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-arm64": "4.1.17", + "@tailwindcss/oxide-darwin-x64": "4.1.17", + "@tailwindcss/oxide-freebsd-x64": "4.1.17", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", + "@tailwindcss/oxide-linux-x64-musl": "4.1.17", + "@tailwindcss/oxide-wasm32-wasi": "4.1.17", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.16.tgz", - "integrity": "sha512-8+ctzkjHgwDJ5caq9IqRSgsP70xhdhJvm+oueS/yhD5ixLhqTw9fSL1OurzMUhBwE5zK26FXLCz2f/RtkISqHA==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz", + "integrity": "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==", "cpu": [ "arm64" ], @@ -3577,9 +3584,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.16.tgz", - "integrity": "sha512-C3oZy5042v2FOALBZtY0JTDnGNdS6w7DxL/odvSny17ORUnaRKhyTse8xYi3yKGyfnTUOdavRCdmc8QqJYwFKA==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz", + "integrity": "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==", "cpu": [ "arm64" ], @@ -3593,9 +3600,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.16.tgz", - "integrity": "sha512-vjrl/1Ub9+JwU6BP0emgipGjowzYZMjbWCDqwA2Z4vCa+HBSpP4v6U2ddejcHsolsYxwL5r4bPNoamlV0xDdLg==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz", + "integrity": "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==", "cpu": [ "x64" ], @@ -3609,9 +3616,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.16.tgz", - "integrity": "sha512-TSMpPYpQLm+aR1wW5rKuUuEruc/oOX3C7H0BTnPDn7W/eMw8W+MRMpiypKMkXZfwH8wqPIRKppuZoedTtNj2tg==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz", + "integrity": "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==", "cpu": [ "x64" ], @@ -3625,9 +3632,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.16.tgz", - "integrity": "sha512-p0GGfRg/w0sdsFKBjMYvvKIiKy/LNWLWgV/plR4lUgrsxFAoQBFrXkZ4C0w8IOXfslB9vHK/JGASWD2IefIpvw==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz", + "integrity": "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==", "cpu": [ "arm" ], @@ -3641,9 +3648,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.16.tgz", - "integrity": "sha512-DoixyMmTNO19rwRPdqviTrG1rYzpxgyYJl8RgQvdAQUzxC1ToLRqtNJpU/ATURSKgIg6uerPw2feW0aS8SNr/w==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz", + "integrity": "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==", "cpu": [ "arm64" ], @@ -3657,9 +3664,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.16.tgz", - "integrity": "sha512-H81UXMa9hJhWhaAUca6bU2wm5RRFpuHImrwXBUvPbYb+3jo32I9VIwpOX6hms0fPmA6f2pGVlybO6qU8pF4fzQ==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz", + "integrity": "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==", "cpu": [ "arm64" ], @@ -3673,9 +3680,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.16.tgz", - "integrity": "sha512-ZGHQxDtFC2/ruo7t99Qo2TTIvOERULPl5l0K1g0oK6b5PGqjYMga+FcY1wIUnrUxY56h28FxybtDEla+ICOyew==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz", + "integrity": "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==", "cpu": [ "x64" ], @@ -3689,9 +3696,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.16.tgz", - "integrity": "sha512-Oi1tAaa0rcKf1Og9MzKeINZzMLPbhxvm7rno5/zuP1WYmpiG0bEHq4AcRUiG2165/WUzvxkW4XDYCscZWbTLZw==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz", + "integrity": "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==", "cpu": [ "x64" ], @@ -3705,9 +3712,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.16.tgz", - "integrity": "sha512-B01u/b8LteGRwucIBmCQ07FVXLzImWESAIMcUU6nvFt/tYsQ6IHz8DmZ5KtvmwxD+iTYBtM1xwoGXswnlu9v0Q==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.17.tgz", + "integrity": "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -3722,8 +3729,8 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.5.0", - "@emnapi/runtime": "^1.5.0", + "@emnapi/core": "^1.6.0", + "@emnapi/runtime": "^1.6.0", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.0.7", "@tybys/wasm-util": "^0.10.1", @@ -3734,9 +3741,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.16.tgz", - "integrity": "sha512-zX+Q8sSkGj6HKRTMJXuPvOcP8XfYON24zJBRPlszcH1Np7xuHXhWn8qfFjIujVzvH3BHU+16jBXwgpl20i+v9A==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.17.tgz", + "integrity": "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==", "cpu": [ "arm64" ], @@ -3750,9 +3757,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.16.tgz", - "integrity": "sha512-m5dDFJUEejbFqP+UXVstd4W/wnxA4F61q8SoL+mqTypId2T2ZpuxosNSgowiCnLp2+Z+rivdU0AqpfgiD7yCBg==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.17.tgz", + "integrity": "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==", "cpu": [ "x64" ], @@ -3766,16 +3773,16 @@ } }, "node_modules/@tailwindcss/postcss": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.16.tgz", - "integrity": "sha512-Qn3SFGPXYQMKR/UtqS+dqvPrzEeBZHrFA92maT4zijCVggdsXnDBMsPFJo1eArX3J+O+Gi+8pV4PkqjLCNBk3A==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.17.tgz", + "integrity": "sha512-+nKl9N9mN5uJ+M7dBOOCzINw94MPstNR/GtIhz1fpZysxL/4a+No64jCBD6CPN+bIHWFx3KWuu8XJRrj/572Dw==", "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.1.16", - "@tailwindcss/oxide": "4.1.16", + "@tailwindcss/node": "4.1.17", + "@tailwindcss/oxide": "4.1.17", "postcss": "^8.4.41", - "tailwindcss": "4.1.16" + "tailwindcss": "4.1.17" } }, "node_modules/@tanstack/react-virtual": { @@ -3816,9 +3823,9 @@ } }, "node_modules/@tauri-apps/cli": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.9.1.tgz", - "integrity": "sha512-kKi2/WWsNXKoMdatBl4xrT7e1Ce27JvsetBVfWuIb6D3ep/Y0WO5SIr70yarXOSWam8NyDur4ipzjZkg6m7VDg==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.9.3.tgz", + "integrity": "sha512-BQ7iLUXTQcyG1PpzLWeVSmBCedYDpnA/6Cm/kRFGtqjTf/eVUlyYO5S2ee07tLum3nWwDBWTGFZeruO8yEukfA==", "dev": true, "license": "Apache-2.0 OR MIT", "bin": { @@ -3832,23 +3839,23 @@ "url": "https://opencollective.com/tauri" }, "optionalDependencies": { - "@tauri-apps/cli-darwin-arm64": "2.9.1", - "@tauri-apps/cli-darwin-x64": "2.9.1", - "@tauri-apps/cli-linux-arm-gnueabihf": "2.9.1", - "@tauri-apps/cli-linux-arm64-gnu": "2.9.1", - "@tauri-apps/cli-linux-arm64-musl": "2.9.1", - "@tauri-apps/cli-linux-riscv64-gnu": "2.9.1", - "@tauri-apps/cli-linux-x64-gnu": "2.9.1", - "@tauri-apps/cli-linux-x64-musl": "2.9.1", - "@tauri-apps/cli-win32-arm64-msvc": "2.9.1", - "@tauri-apps/cli-win32-ia32-msvc": "2.9.1", - "@tauri-apps/cli-win32-x64-msvc": "2.9.1" + "@tauri-apps/cli-darwin-arm64": "2.9.3", + "@tauri-apps/cli-darwin-x64": "2.9.3", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.9.3", + "@tauri-apps/cli-linux-arm64-gnu": "2.9.3", + "@tauri-apps/cli-linux-arm64-musl": "2.9.3", + "@tauri-apps/cli-linux-riscv64-gnu": "2.9.3", + "@tauri-apps/cli-linux-x64-gnu": "2.9.3", + "@tauri-apps/cli-linux-x64-musl": "2.9.3", + "@tauri-apps/cli-win32-arm64-msvc": "2.9.3", + "@tauri-apps/cli-win32-ia32-msvc": "2.9.3", + "@tauri-apps/cli-win32-x64-msvc": "2.9.3" } }, "node_modules/@tauri-apps/cli-darwin-arm64": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.9.1.tgz", - "integrity": "sha512-sdwhtsE/6njD0AjgfYEj1JyxZH4SBmCJSXpRm6Ph5fQeuZD6MyjzjdVOrrtFguyREVQ7xn0Ujkwvbo01ULthNg==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.9.3.tgz", + "integrity": "sha512-W8FQXZXQmQ0Fmj9UJXNrm2mLdIaLLriKVY7o/FzmizyIKTPIvHjfZALTNybbpTQRbJvKoGHLrW1DNzAWVDWJYg==", "cpu": [ "arm64" ], @@ -3863,9 +3870,9 @@ } }, "node_modules/@tauri-apps/cli-darwin-x64": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.9.1.tgz", - "integrity": "sha512-c86g+67wTdI4TUCD7CaSd/13+oYuLQxVST4ZNJ5C+6i1kdnU3Us1L68N9MvbDLDQGJc9eo0pvuK6sCWkee+BzA==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.9.3.tgz", + "integrity": "sha512-zDwu40rlshijt3TU6aRvzPUyVpapsx1sNfOlreDMTaMelQLHl6YoQzSRpLHYwrHrhimxyX2uDqnKIiuGel0Lhg==", "cpu": [ "x64" ], @@ -3880,9 +3887,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.9.1.tgz", - "integrity": "sha512-IrB3gFQmueQKJjjisOcMktW/Gh6gxgqYO419doA3YZ7yIV5rbE8ZW52Q3I4AO+SlFEyVYer5kpi066p0JBlLGw==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.9.3.tgz", + "integrity": "sha512-+Oc2OfcTRwYtW93VJqd/HOk77buORwC9IToj/qsEvM7bTMq6Kda4alpZprzwrCHYANSw+zD8PgjJdljTpe4p+g==", "cpu": [ "arm" ], @@ -3897,9 +3904,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-gnu": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.9.1.tgz", - "integrity": "sha512-Ke7TyXvu6HbWSkmVkFbbH19D3cLsd117YtXP/u9NIvSpYwKeFtnbpirrIUfPm44Q+PZFZ2Hvg8X9qoUiAK0zKw==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.9.3.tgz", + "integrity": "sha512-59GqU/J1n9wFyAtleoQOaU0oVIo+kwQynEw4meFDoKRXszKGor6lTsbsS3r0QKLSPbc0o/yYGJhqqCtkYjb/eg==", "cpu": [ "arm64" ], @@ -3914,9 +3921,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-musl": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.9.1.tgz", - "integrity": "sha512-sGvy75sv55oeMulR5ArwPD28DsDQxqTzLhXCrpU9/nbFg/JImmI7k994YE9fr3V0qE3Cjk5gjLldRNv7I9sjwQ==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.9.3.tgz", + "integrity": "sha512-fzvG+jEn5/iYGNH6Z2IRMheYFC4pJdXa19BR9fFm6Bdn2cuajRLDKdUcEME/DCtwqclphXtFZTrT4oezY5vI/A==", "cpu": [ "arm64" ], @@ -3931,9 +3938,9 @@ } }, "node_modules/@tauri-apps/cli-linux-riscv64-gnu": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.9.1.tgz", - "integrity": "sha512-tEKbJydV3BdIxpAx8aGHW6VDg1xW4LlQuRD/QeFZdZNTreHJpMbJEcdvAcI+Hg6vgQpVpaoEldR9W4F6dYSLqQ==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.9.3.tgz", + "integrity": "sha512-qV8DZXI/fZwawk6T3Th1g6smiNC2KeQTk7XFgKvqZ6btC01z3UTsQmNGvI602zwm3Ld1TBZb4+rEWu2QmQimmw==", "cpu": [ "riscv64" ], @@ -3948,9 +3955,9 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-gnu": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.9.1.tgz", - "integrity": "sha512-mg5msXHagtHpyCVWgI01M26JeSrgE/otWyGdYcuTwyRYZYEJRTbcNt7hscOkdNlPBe7isScW7PVKbxmAjJJl4g==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.9.3.tgz", + "integrity": "sha512-tquyEONCNRfqEBWEe4eAHnxFN5yY5lFkCuD4w79XLIovUxVftQ684+xLp7zkhntkt4y20SMj2AgJa/+MOlx4Kg==", "cpu": [ "x64" ], @@ -3965,9 +3972,9 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-musl": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.9.1.tgz", - "integrity": "sha512-lFZEXkpDreUe3zKilvnMsrnKP9gwQudaEjDnOz/GMzbzNceIuPfFZz0cR/ky1Aoq4eSvZonPKHhROq4owz4fzg==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.9.3.tgz", + "integrity": "sha512-v2cBIB/6ji8DL+aiL5QUykU3ZO8OoJGyx50/qv2HQVzkf85KdaYSis3D/oVRemN/pcDz+vyCnnL3XnzFnDl4JQ==", "cpu": [ "x64" ], @@ -3982,9 +3989,9 @@ } }, "node_modules/@tauri-apps/cli-win32-arm64-msvc": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.9.1.tgz", - "integrity": "sha512-ejc5RAp/Lm1Aj0EQHaT+Wdt5PHfdgQV5hIDV00MV6HNbIb5W4ZUFxMDaRkAg65gl9MvY2fH396riePW3RoKXDw==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.9.3.tgz", + "integrity": "sha512-ZGvBy7nvrHPbE0HeKp/ioaiw8bNgAHxWnb7JRZ4/G0A+oFj0SeSFxl9k5uU6FKnM7bHM23Gd1oeaDex9g5Fceg==", "cpu": [ "arm64" ], @@ -3999,9 +4006,9 @@ } }, "node_modules/@tauri-apps/cli-win32-ia32-msvc": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.9.1.tgz", - "integrity": "sha512-fSATtJDc0fNjVB6ystyi8NbwhNFk8i8E05h6KrsC8Fio5eaJIJvPCbC9pdrPl6kkxN1X7fj25ErBbgfqgcK8Fg==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.9.3.tgz", + "integrity": "sha512-UsgIwOnpCoY9NK9/65QiwgmWVIE80LE7SwRYVblGtmlY9RYfsYvpbItwsovA/AcHMTiO+OCvS/q9yLeqS3m6Sg==", "cpu": [ "ia32" ], @@ -4016,9 +4023,9 @@ } }, "node_modules/@tauri-apps/cli-win32-x64-msvc": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.9.1.tgz", - "integrity": "sha512-/JHlOzpUDhjBOO9w167bcYxfJbcMQv7ykS/Y07xjtcga8np0rzUzVGWYmLMH7orKcDMC7wjhheEW1x8cbGma/Q==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.9.3.tgz", + "integrity": "sha512-fmw7NrrHE5m49idCvJAx9T9bsupjdJ0a3p3DPCNCZRGANU6R1tA1L+KTlVuUtdAldX2NqU/9UPo2SCslYKgJHQ==", "cpu": [ "x64" ], @@ -4353,9 +4360,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz", - "integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==", + "version": "24.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz", + "integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==", "license": "MIT", "dependencies": { "undici-types": "~7.16.0" @@ -4428,17 +4435,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.2.tgz", - "integrity": "sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.3.tgz", + "integrity": "sha512-sbaQ27XBUopBkRiuY/P9sWGOWUW4rl8fDoHIUmLpZd8uldsTyB4/Zg6bWTegPoTLnKj9Hqgn3QD6cjPNB32Odw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.46.2", - "@typescript-eslint/type-utils": "8.46.2", - "@typescript-eslint/utils": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2", + "@typescript-eslint/scope-manager": "8.46.3", + "@typescript-eslint/type-utils": "8.46.3", + "@typescript-eslint/utils": "8.46.3", + "@typescript-eslint/visitor-keys": "8.46.3", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -4452,22 +4459,22 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.46.2", + "@typescript-eslint/parser": "^8.46.3", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.2.tgz", - "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.3.tgz", + "integrity": "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.46.2", - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/typescript-estree": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2", + "@typescript-eslint/scope-manager": "8.46.3", + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/typescript-estree": "8.46.3", + "@typescript-eslint/visitor-keys": "8.46.3", "debug": "^4.3.4" }, "engines": { @@ -4483,14 +4490,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.2.tgz", - "integrity": "sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.3.tgz", + "integrity": "sha512-Fz8yFXsp2wDFeUElO88S9n4w1I4CWDTXDqDr9gYvZgUpwXQqmZBr9+NTTql5R3J7+hrJZPdpiWaB9VNhAKYLuQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.46.2", - "@typescript-eslint/types": "^8.46.2", + "@typescript-eslint/tsconfig-utils": "^8.46.3", + "@typescript-eslint/types": "^8.46.3", "debug": "^4.3.4" }, "engines": { @@ -4505,14 +4512,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.2.tgz", - "integrity": "sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.3.tgz", + "integrity": "sha512-FCi7Y1zgrmxp3DfWfr+3m9ansUUFoy8dkEdeQSgA9gbm8DaHYvZCdkFRQrtKiedFf3Ha6VmoqoAaP68+i+22kg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2" + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/visitor-keys": "8.46.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4523,9 +4530,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.2.tgz", - "integrity": "sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.3.tgz", + "integrity": "sha512-GLupljMniHNIROP0zE7nCcybptolcH8QZfXOpCfhQDAdwJ/ZTlcaBOYebSOZotpti/3HrHSw7D3PZm75gYFsOA==", "dev": true, "license": "MIT", "engines": { @@ -4540,15 +4547,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.2.tgz", - "integrity": "sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.3.tgz", + "integrity": "sha512-ZPCADbr+qfz3aiTTYNNkCbUt+cjNwI/5McyANNrFBpVxPt7GqpEYz5ZfdwuFyGUnJ9FdDXbGODUu6iRCI6XRXw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/typescript-estree": "8.46.2", - "@typescript-eslint/utils": "8.46.2", + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/typescript-estree": "8.46.3", + "@typescript-eslint/utils": "8.46.3", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -4565,9 +4572,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.2.tgz", - "integrity": "sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.3.tgz", + "integrity": "sha512-G7Ok9WN/ggW7e/tOf8TQYMaxgID3Iujn231hfi0Pc7ZheztIJVpO44ekY00b7akqc6nZcvregk0Jpah3kep6hA==", "dev": true, "license": "MIT", "engines": { @@ -4579,16 +4586,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.2.tgz", - "integrity": "sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.3.tgz", + "integrity": "sha512-f/NvtRjOm80BtNM5OQtlaBdM5BRFUv7gf381j9wygDNL+qOYSNOgtQ/DCndiYi80iIOv76QqaTmp4fa9hwI0OA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.46.2", - "@typescript-eslint/tsconfig-utils": "8.46.2", - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2", + "@typescript-eslint/project-service": "8.46.3", + "@typescript-eslint/tsconfig-utils": "8.46.3", + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/visitor-keys": "8.46.3", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -4608,16 +4615,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.2.tgz", - "integrity": "sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.3.tgz", + "integrity": "sha512-VXw7qmdkucEx9WkmR3ld/u6VhRyKeiF1uxWwCy/iuNfokjJ7VhsgLSOTjsol8BunSw190zABzpwdNsze2Kpo4g==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.46.2", - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/typescript-estree": "8.46.2" + "@typescript-eslint/scope-manager": "8.46.3", + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/typescript-estree": "8.46.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4632,13 +4639,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.2.tgz", - "integrity": "sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.3.tgz", + "integrity": "sha512-uk574k8IU0rOF/AjniX8qbLSGURJVUCeM5e4MIMKBFFi8weeiLrG1fyQejyLXQpRZbU/1BuQasleV/RfHC3hHg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.2", + "@typescript-eslint/types": "8.46.3", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -4932,13 +4939,13 @@ ] }, "node_modules/@vitejs/plugin-react-swc": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-4.2.0.tgz", - "integrity": "sha512-/tesahXD1qpkGC6FzMoFOJj0RyZdw9xLELOL+6jbElwmWfwOnIVy+IfpY+o9JfD9PKaR/Eyb6DNrvbXpuvA+8Q==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-4.2.1.tgz", + "integrity": "sha512-SIZ/XxeS2naLw4L2vVvpTyujM2OY+Rf+y6nWETqfoBrZpI3SFdyNJof3nQ8HbLhXJ1Eh9e9c0JGYC8GYPhLkCw==", "dev": true, "license": "MIT", "dependencies": { - "@rolldown/pluginutils": "1.0.0-beta.43", + "@rolldown/pluginutils": "1.0.0-beta.46", "@swc/core": "^1.13.5" }, "engines": { @@ -5098,13 +5105,13 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.22", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.22.tgz", - "integrity": "sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.24.tgz", + "integrity": "sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.4", - "@vue/shared": "3.5.22", + "@babel/parser": "^7.28.5", + "@vue/shared": "3.5.24", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" @@ -5129,28 +5136,28 @@ "license": "MIT" }, "node_modules/@vue/compiler-dom": { - "version": "3.5.22", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.22.tgz", - "integrity": "sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.24.tgz", + "integrity": "sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==", "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.22", - "@vue/shared": "3.5.22" + "@vue/compiler-core": "3.5.24", + "@vue/shared": "3.5.24" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.22", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.22.tgz", - "integrity": "sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.24.tgz", + "integrity": "sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.4", - "@vue/compiler-core": "3.5.22", - "@vue/compiler-dom": "3.5.22", - "@vue/compiler-ssr": "3.5.22", - "@vue/shared": "3.5.22", + "@babel/parser": "^7.28.5", + "@vue/compiler-core": "3.5.24", + "@vue/compiler-dom": "3.5.24", + "@vue/compiler-ssr": "3.5.24", + "@vue/shared": "3.5.24", "estree-walker": "^2.0.2", - "magic-string": "^0.30.19", + "magic-string": "^0.30.21", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } @@ -5162,67 +5169,67 @@ "license": "MIT" }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.22", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.22.tgz", - "integrity": "sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.24.tgz", + "integrity": "sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.22", - "@vue/shared": "3.5.22" + "@vue/compiler-dom": "3.5.24", + "@vue/shared": "3.5.24" } }, "node_modules/@vue/reactivity": { - "version": "3.5.22", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.22.tgz", - "integrity": "sha512-f2Wux4v/Z2pqc9+4SmgZC1p73Z53fyD90NFWXiX9AKVnVBEvLFOWCEgJD3GdGnlxPZt01PSlfmLqbLYzY/Fw4A==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.24.tgz", + "integrity": "sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg==", "license": "MIT", "peer": true, "dependencies": { - "@vue/shared": "3.5.22" + "@vue/shared": "3.5.24" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.22", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.22.tgz", - "integrity": "sha512-EHo4W/eiYeAzRTN5PCextDUZ0dMs9I8mQ2Fy+OkzvRPUYQEyK9yAjbasrMCXbLNhF7P0OUyivLjIy0yc6VrLJQ==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.24.tgz", + "integrity": "sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ==", "license": "MIT", "peer": true, "dependencies": { - "@vue/reactivity": "3.5.22", - "@vue/shared": "3.5.22" + "@vue/reactivity": "3.5.24", + "@vue/shared": "3.5.24" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.22", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.22.tgz", - "integrity": "sha512-Av60jsryAkI023PlN7LsqrfPvwfxOd2yAwtReCjeuugTJTkgrksYJJstg1e12qle0NarkfhfFu1ox2D+cQotww==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.24.tgz", + "integrity": "sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw==", "license": "MIT", "peer": true, "dependencies": { - "@vue/reactivity": "3.5.22", - "@vue/runtime-core": "3.5.22", - "@vue/shared": "3.5.22", + "@vue/reactivity": "3.5.24", + "@vue/runtime-core": "3.5.24", + "@vue/shared": "3.5.24", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.22", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.22.tgz", - "integrity": "sha512-gXjo+ao0oHYTSswF+a3KRHZ1WszxIqO7u6XwNHqcqb9JfyIL/pbWrrh/xLv7jeDqla9u+LK7yfZKHih1e1RKAQ==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.24.tgz", + "integrity": "sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w==", "license": "MIT", "peer": true, "dependencies": { - "@vue/compiler-ssr": "3.5.22", - "@vue/shared": "3.5.22" + "@vue/compiler-ssr": "3.5.24", + "@vue/shared": "3.5.24" }, "peerDependencies": { - "vue": "3.5.22" + "vue": "3.5.24" } }, "node_modules/@vue/shared": { - "version": "3.5.22", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.22.tgz", - "integrity": "sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.24.tgz", + "integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==", "license": "MIT" }, "node_modules/abbrev": { @@ -5629,9 +5636,9 @@ } }, "node_modules/axios": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.0.tgz", - "integrity": "sha512-zt40Pz4zcRXra9CVV31KeyofwiNvAbJ5B6YPz9pMJ+yOSLikvPT4Yi5LjfgjRa9CawVYBaD1JQzIVcIvBejKeA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -5687,9 +5694,9 @@ "license": "MIT" }, "node_modules/bare-events": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.1.tgz", - "integrity": "sha512-oxSAxTS1hRfnyit2CL5QpAOS5ixfBjj6ex3yTNvXyY/kE719jQ/IjuESJBK2w5v4wwQRAHGseVJXx9QBYOtFGQ==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", "dev": true, "license": "Apache-2.0", "peerDependencies": { @@ -5773,9 +5780,9 @@ } }, "node_modules/bare-url": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.1.tgz", - "integrity": "sha512-v2yl0TnaZTdEnelkKtXZGnotiV6qATBlnNuUMrHl6v9Lmmrh9mw9RYyImPU7/4RahumSwQS1k2oKXcRfXcbjJw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz", + "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -5805,9 +5812,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.20", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.20.tgz", - "integrity": "sha512-JMWsdF+O8Orq3EMukbUN1QfbLK9mX2CkUmQBcW2T0s8OmdAUL5LLM/6wFwSrqXzlXB13yhyK9gTKS1rIizOduQ==", + "version": "2.8.25", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.25.tgz", + "integrity": "sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.js" @@ -5970,6 +5977,18 @@ "node": "*" } }, + "node_modules/byte-counter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/byte-counter/-/byte-counter-0.1.0.tgz", + "integrity": "sha512-jheRLVMeUKrDBjVw2O5+k4EvR4t9wtxHL+bo/LxfkxsVeuGMy3a5SEGgXdAFA4FSzTrU8rQXQIrsZ3oBq5a0pQ==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -5990,9 +6009,9 @@ } }, "node_modules/cacheable-request": { - "version": "13.0.12", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-13.0.12.tgz", - "integrity": "sha512-qqK/etGeI/9DV5yRkO50ApDTjip9UXPml1NHYJksUAw15yMLOf8VUO1/8bu4P8birOCqR+hYQ/nh1Lezc8sZrA==", + "version": "13.0.13", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-13.0.13.tgz", + "integrity": "sha512-/5/6xDTN/AZHOn93MNaU7HUSzOL/YJWOwa5hV84ib1mPUtQB/ZTg5bn57UGfdNwXGhgQfcPpJDy+eGiNgGJI1w==", "license": "MIT", "dependencies": { "@types/http-cache-semantics": "^4.0.4", @@ -6100,9 +6119,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001751", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz", - "integrity": "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==", + "version": "1.0.30001754", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001754.tgz", + "integrity": "sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==", "funding": [ { "type": "opencollective", @@ -6447,7 +6466,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -6493,9 +6511,9 @@ } }, "node_modules/cssstyle": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.1.tgz", - "integrity": "sha512-g5PC9Aiph9eiczFpcgUhd9S4UUO3F+LHGRIi5NUMZ+4xtoIYbHNZwZnWA2JsFGe8OU8nl4WyaEFiZuGuxlutJQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.2.tgz", + "integrity": "sha512-zDMqXh8Vs1CdRYZQ2M633m/SFgcjlu8RB8b/1h82i+6vpArF507NSYIWJHGlJaTWoS+imcnctmEz43txhbVkOw==", "dev": true, "license": "MIT", "dependencies": { @@ -6592,9 +6610,9 @@ } }, "node_modules/dayjs": { - "version": "1.11.18", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz", - "integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==", + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", "license": "MIT", "peer": true }, @@ -6962,9 +6980,9 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1508733", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1508733.tgz", - "integrity": "sha512-QJ1R5gtck6nDcdM+nlsaJXcelPEI7ZxSMw1ujHpO1c4+9l+Nue5qlebi9xO1Z2MGr92bFOQTW7/rrheh5hHxDg==", + "version": "0.0.1521046", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1521046.tgz", + "integrity": "sha512-vhE6eymDQSKWUXwwA37NtTTVEzjtGVfDr3pRbsWEQ5onH/Snp2c+2xZHWJJawG/0hCCJLRGt4xVtEVUVILol4w==", "dev": true, "license": "BSD-3-Clause" }, @@ -7031,9 +7049,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.241", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.241.tgz", - "integrity": "sha512-ILMvKX/ZV5WIJzzdtuHg8xquk2y0BOGlFOxBVwTpbiXqWIH0hamG45ddU4R3PQ0gYu+xgo0vdHXHli9sHIGb4w==", + "version": "1.5.248", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.248.tgz", + "integrity": "sha512-zsur2yunphlyAO4gIubdJEXCK6KOVvtpiuDfCIqbM9FjcnMYiyn0ICa3hWfPr0nc41zcLWobgy1iL7VvoOyA2Q==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -7260,9 +7278,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.11", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz", - "integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -7273,32 +7291,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.11", - "@esbuild/android-arm": "0.25.11", - "@esbuild/android-arm64": "0.25.11", - "@esbuild/android-x64": "0.25.11", - "@esbuild/darwin-arm64": "0.25.11", - "@esbuild/darwin-x64": "0.25.11", - "@esbuild/freebsd-arm64": "0.25.11", - "@esbuild/freebsd-x64": "0.25.11", - "@esbuild/linux-arm": "0.25.11", - "@esbuild/linux-arm64": "0.25.11", - "@esbuild/linux-ia32": "0.25.11", - "@esbuild/linux-loong64": "0.25.11", - "@esbuild/linux-mips64el": "0.25.11", - "@esbuild/linux-ppc64": "0.25.11", - "@esbuild/linux-riscv64": "0.25.11", - "@esbuild/linux-s390x": "0.25.11", - "@esbuild/linux-x64": "0.25.11", - "@esbuild/netbsd-arm64": "0.25.11", - "@esbuild/netbsd-x64": "0.25.11", - "@esbuild/openbsd-arm64": "0.25.11", - "@esbuild/openbsd-x64": "0.25.11", - "@esbuild/openharmony-arm64": "0.25.11", - "@esbuild/sunos-x64": "0.25.11", - "@esbuild/win32-arm64": "0.25.11", - "@esbuild/win32-ia32": "0.25.11", - "@esbuild/win32-x64": "0.25.11" + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" } }, "node_modules/escalade": { @@ -7356,20 +7374,20 @@ } }, "node_modules/eslint": { - "version": "9.38.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.38.0.tgz", - "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", + "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", - "@eslint/config-helpers": "^0.4.1", - "@eslint/core": "^0.16.0", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.38.0", - "@eslint/plugin-kit": "^0.4.0", + "@eslint/js": "9.39.1", + "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -7759,9 +7777,9 @@ } }, "node_modules/esrap": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.1.1.tgz", - "integrity": "sha512-ebTT9B6lOtZGMgJ3o5r12wBacHctG7oEWazIda8UlPfA3HD/Wrv8FdXoVo73vzdpwCxNyXjPauyN2bbJzMkB9A==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.1.2.tgz", + "integrity": "sha512-DgvlIQeowRNyvLPWW4PT7Gu13WznY288Du086E751mwwbsgr29ytBiYeLzAGIo0qk3Ujob0SDk8TiSaM5WQzNg==", "license": "MIT", "peer": true, "dependencies": { @@ -8525,9 +8543,9 @@ } }, "node_modules/globals": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", - "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", "license": "MIT", "engines": { "node": ">=18" @@ -8589,13 +8607,14 @@ } }, "node_modules/got": { - "version": "14.6.1", - "resolved": "https://registry.npmjs.org/got/-/got-14.6.1.tgz", - "integrity": "sha512-56lZOw904LHKr6KdKN0Zbgz9Lw6cpEAAqZcS+0iY4D27caHoLiFT0EGCbrX9ZKYvt+I2lGl3a8eeDNSbmhyjkQ==", + "version": "14.6.2", + "resolved": "https://registry.npmjs.org/got/-/got-14.6.2.tgz", + "integrity": "sha512-bnhvxegqufyxHAmzwCZSscjGLVpw6/NzTXOk2tQVu/b9Q9FeMAgLabYulXEQRwP04UYltnkcZwvBq14fsdqvyw==", "license": "MIT", "dependencies": { "@sindresorhus/is": "^7.0.1", "@szmarczak/http-timer": "^5.0.1", + "byte-counter": "^0.1.0", "cacheable-lookup": "^7.0.0", "cacheable-request": "^13.0.12", "decompress-response": "^10.0.0", @@ -8835,9 +8854,9 @@ } }, "node_modules/i18next": { - "version": "25.6.0", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.6.0.tgz", - "integrity": "sha512-tTn8fLrwBYtnclpL5aPXK/tAYBLWVvoHM1zdfXoRNLcI+RvtMsoZRV98ePlaW3khHYKuNh/Q65W/+NVFUeIwVw==", + "version": "25.6.1", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.6.1.tgz", + "integrity": "sha512-yUWvdXtalZztmKrKw3yz/AvSP3yKyqIkVPx/wyvoYy9lkLmwzItLxp0iHZLG5hfVQ539Jor4XLO+U+NHIXg7pw==", "funding": [ { "type": "individual", @@ -9553,7 +9572,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/istanbul-lib-coverage": { @@ -9655,14 +9673,15 @@ } }, "node_modules/jsdom": { - "version": "27.0.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.0.1.tgz", - "integrity": "sha512-SNSQteBL1IlV2zqhwwolaG9CwhIhTvVHWg3kTss/cLE7H/X4644mtPQqYvCfsSrGQWt9hSZcgOXX8bOZaMN+kA==", + "version": "27.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.1.0.tgz", + "integrity": "sha512-Pcfm3eZ+eO4JdZCXthW9tCDT3nF4K+9dmeZ+5X39n+Kqz0DDIABRP5CAEOHRFZk8RGuC2efksTJxrjp8EXCunQ==", "dev": true, "license": "MIT", "dependencies": { - "@asamuzakjp/dom-selector": "^6.7.2", - "cssstyle": "^5.3.1", + "@acemir/cssom": "^0.9.19", + "@asamuzakjp/dom-selector": "^6.7.3", + "cssstyle": "^5.3.2", "data-urls": "^6.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^4.0.0", @@ -9670,7 +9689,6 @@ "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "parse5": "^8.0.0", - "rrweb-cssom": "^0.8.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.0", @@ -9683,7 +9701,7 @@ "xml-name-validator": "^5.0.0" }, "engines": { - "node": ">=20" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { "canvas": "^3.0.0" @@ -10742,9 +10760,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.26", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.26.tgz", - "integrity": "sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA==", + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "license": "MIT" }, "node_modules/node-source-walk": { @@ -11231,7 +11249,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -11306,15 +11323,15 @@ } }, "node_modules/pdfjs-dist": { - "version": "5.4.296", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.296.tgz", - "integrity": "sha512-DlOzet0HO7OEnmUmB6wWGJrrdvbyJKftI1bhMitK7O2N8W2gc757yyYBbINy9IDafXAV9wmKr9t7xsTaNKRG5Q==", + "version": "5.4.394", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.394.tgz", + "integrity": "sha512-9ariAYGqUJzx+V/1W4jHyiyCep6IZALmDzoaTLZ6VNu8q9LWi1/ukhzHgE2Xsx96AZi0mbZuK4/ttIbqSbLypg==", "license": "Apache-2.0", "engines": { "node": ">=20.16.0 || >=22.3.0" }, "optionalDependencies": { - "@napi-rs/canvas": "^0.1.80" + "@napi-rs/canvas": "^0.1.81" } }, "node_modules/pend": { @@ -11692,12 +11709,12 @@ } }, "node_modules/posthog-js": { - "version": "1.281.0", - "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.281.0.tgz", - "integrity": "sha512-t3sAlgVozpU1W1ppiF5zLG6eBRPUs0hmtxN8R1V7P0qZFmnECshAAk2cBxCsxEanadT3iUpS8Z7crBytATqWQQ==", + "version": "1.289.0", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.289.0.tgz", + "integrity": "sha512-fv1ClV/yqwub5zs3QbrO7Jqi50StGaUAcCE5xwmlsnMxh3GgwMHip7waRk34T/9LhAI7rLXT7ykGOrIEULANjA==", "license": "SEE LICENSE IN LICENSE", "dependencies": { - "@posthog/core": "1.4.0", + "@posthog/core": "1.5.2", "core-js": "^3.38.1", "fflate": "^0.4.8", "preact": "^10.19.3", @@ -11922,18 +11939,18 @@ } }, "node_modules/puppeteer": { - "version": "24.26.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.26.1.tgz", - "integrity": "sha512-3RG2UqclzMFolM2fS4bN8t5/EjZ0VwEoAGVxG8PMGeprjLzj+x0U4auH7MQ4B6ftW+u1JUnTTN8ab4ABPdl4mA==", + "version": "24.29.1", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.29.1.tgz", + "integrity": "sha512-pX05JV1mMP+1N0vP3I4DOVwjMdpihv2LxQTtSfw6CUm5F0ZFLUFE/LSZ4yUWHYaM3C11Hdu+sgn7uY7teq5MYw==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.10.12", + "@puppeteer/browsers": "2.10.13", "chromium-bidi": "10.5.1", "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1508733", - "puppeteer-core": "24.26.1", + "devtools-protocol": "0.0.1521046", + "puppeteer-core": "24.29.1", "typed-query-selector": "^2.12.0" }, "bin": { @@ -11944,16 +11961,16 @@ } }, "node_modules/puppeteer-core": { - "version": "24.26.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.26.1.tgz", - "integrity": "sha512-YHZdo3chJ5b9pTYVnuDuoI3UX/tWJFJyRZvkLbThGy6XeHWC+0KI8iN0UMCkvde5l/YOk3huiVZ/PvwgSbwdrA==", + "version": "24.29.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.29.1.tgz", + "integrity": "sha512-ErJ9qKCK+bdLvBa7QVSQTBSPm8KZbl1yC/WvhrZ0ut27hDf2QBzjDsn1IukzE1i1KtZ7NYGETOV4W1beoo9izA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.10.12", + "@puppeteer/browsers": "2.10.13", "chromium-bidi": "10.5.1", "debug": "^4.4.3", - "devtools-protocol": "0.0.1508733", + "devtools-protocol": "0.0.1521046", "typed-query-selector": "^2.12.0", "webdriver-bidi-protocol": "0.3.8", "ws": "^8.18.3" @@ -12276,9 +12293,9 @@ "license": "0BSD" }, "node_modules/react-router": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.4.tgz", - "integrity": "sha512-SD3G8HKviFHg9xj7dNODUKDFgpG4xqD5nhyd0mYoB5iISepuZAvzSr8ywxgxKJ52yRzf/HWtVHc9AWwoTbljvA==", + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.5.tgz", + "integrity": "sha512-JmxqrnBZ6E9hWmf02jzNn9Jm3UqyeimyiwzD69NjxGySG6lIz/1LVPsoTCwN7NBX2XjCEa1LIX5EMz1j2b6u6A==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -12298,12 +12315,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.4.tgz", - "integrity": "sha512-f30P6bIkmYvnHHa5Gcu65deIXoA2+r3Eb6PJIAddvsT9aGlchMatJ51GgpU470aSqRRbFX22T70yQNUGuW3DfA==", + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.5.tgz", + "integrity": "sha512-mkEmq/K8tKN63Ae2M7Xgz3c9l9YNbY+NHH6NNeUmLA3kDkhKXRsNb/ZpxaEunvGo2/3YXdk5EJU3Hxp3ocaBPw==", "license": "MIT", "dependencies": { - "react-router": "7.9.4" + "react-router": "7.9.5" }, "engines": { "node": ">=20.0.0" @@ -12718,13 +12735,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/rrweb-cssom": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", - "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", - "dev": true, - "license": "MIT" - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -12954,7 +12964,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -12967,7 +12976,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -13584,9 +13592,9 @@ } }, "node_modules/svelte": { - "version": "5.43.0", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.43.0.tgz", - "integrity": "sha512-1sRxVbgJAB+UGzwkc3GUoiBSzEOf0jqzccMaVoI2+pI+kASUe9qubslxace8+Mzhqw19k4syTA5niCIJwfXpOA==", + "version": "5.43.4", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.43.4.tgz", + "integrity": "sha512-tPNp21nDWB0PSHE+VrTvEy9cFtDp2Q+ATxQoFomISEVdikZ1QZ69UqBPz/LlT+Oc8/LYS/COYwDQZrmZEUr+JQ==", "license": "MIT", "peer": true, "dependencies": { @@ -13661,9 +13669,9 @@ } }, "node_modules/tailwindcss": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.16.tgz", - "integrity": "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==", + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", + "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", "license": "MIT" }, "node_modules/tapable": { @@ -13773,11 +13781,14 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", - "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/tinyglobby": { "version": "0.2.15", @@ -14130,16 +14141,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.2.tgz", - "integrity": "sha512-vbw8bOmiuYNdzzV3lsiWv6sRwjyuKJMQqWulBOU7M0RrxedXledX8G8kBbQeiOYDnTfiXz0Y4081E1QMNB6iQg==", + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.3.tgz", + "integrity": "sha512-bAfgMavTuGo+8n6/QQDVQz4tZ4f7Soqg53RbrlZQEoAltYop/XR4RAts/I0BrO3TTClTSTFJ0wYbla+P8cEWJA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.46.2", - "@typescript-eslint/parser": "8.46.2", - "@typescript-eslint/typescript-estree": "8.46.2", - "@typescript-eslint/utils": "8.46.2" + "@typescript-eslint/eslint-plugin": "8.46.3", + "@typescript-eslint/parser": "8.46.3", + "@typescript-eslint/typescript-estree": "8.46.3", + "@typescript-eslint/utils": "8.46.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -14401,9 +14412,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "7.1.12", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz", - "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz", + "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -14667,17 +14678,17 @@ } }, "node_modules/vue": { - "version": "3.5.22", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.22.tgz", - "integrity": "sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==", + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.24.tgz", + "integrity": "sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==", "license": "MIT", "peer": true, "dependencies": { - "@vue/compiler-dom": "3.5.22", - "@vue/compiler-sfc": "3.5.22", - "@vue/runtime-dom": "3.5.22", - "@vue/server-renderer": "3.5.22", - "@vue/shared": "3.5.22" + "@vue/compiler-dom": "3.5.24", + "@vue/compiler-sfc": "3.5.24", + "@vue/runtime-dom": "3.5.24", + "@vue/server-renderer": "3.5.24", + "@vue/shared": "3.5.24" }, "peerDependencies": { "typescript": "*" @@ -14785,7 +14796,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" diff --git a/frontend/public/branding/StirlingLogo.svg b/frontend/public/branding/StirlingLogo.svg new file mode 100644 index 000000000..db1f03e00 --- /dev/null +++ b/frontend/public/branding/StirlingLogo.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/frontend/public/branding/StirlingLogoLegacy.svg b/frontend/public/branding/StirlingLogoLegacy.svg new file mode 100644 index 000000000..29a85fbef --- /dev/null +++ b/frontend/public/branding/StirlingLogoLegacy.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/public/branding/StirlingPDFLogoNoTextLightHC.svg b/frontend/public/branding/StirlingPDFLogoNoTextLightHC.svg new file mode 100644 index 000000000..a909d6016 --- /dev/null +++ b/frontend/public/branding/StirlingPDFLogoNoTextLightHC.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/public/locales/en-GB/translation.json b/frontend/public/locales/en-GB/translation.json index bf287c732..29317af86 100644 --- a/frontend/public/locales/en-GB/translation.json +++ b/frontend/public/locales/en-GB/translation.json @@ -294,7 +294,8 @@ "learnMore": "Learn more", "enable": "Enable analytics", "disable": "Disable analytics", - "settings": "You can change the settings for analytics in the config/settings.yml file" + "settings": "You can change the settings for analytics in the config/settings.yml file", + "privacyAssurance": "We do not track any personal information or the contents of your files." }, "navbar": { "favorite": "Favorites", @@ -5123,6 +5124,10 @@ "maybeLater": "Maybe Later", "dontShowAgain": "Don't Show Again" }, + "welcomeSlide": { + "title": "Welcome to Stirling", + "body": "Stirling PDF is now ready for teams of all sizes. This update includes a new layout, powerful new admin capabilities, and our most requested feature - Edit Text." + }, "allTools": "This is the Tools panel, where you can browse and select from all available PDF tools.", "selectCropTool": "Let's select the Crop tool to demonstrate how to use one of the tools.", "toolInterface": "This is the Crop tool interface. As you can see, there's not much there because we haven't added any PDF files to work with yet.", @@ -5145,7 +5150,39 @@ "next": "Next", "finish": "Finish", "startTour": "Start Tour", - "startTourDescription": "Take a guided tour of Stirling PDF's key features" + "startTourDescription": "Take a guided tour of Stirling PDF's key features", + "buttons": { + "next": "Next →", + "back": "Back", + "skipForNow": "Skip for now", + "download": "Download →", + "showMeAround": "Show me around", + "skipTheTour": "Skip the tour" + }, + "serverLicense": { + "skip": "Skip for now", + "seePlans": "See Plans →", + "upgrade": "Upgrade now →", + "freeTitle": "Server License", + "overLimitTitle": "Server License Needed", + "overLimitBody": "Our licensing permits up to {{freeTierLimit}} users for free per server. You have {{overLimitUserCopy}} Stirling users. To continue uninterrupted, upgrade to the Stirling Server plan - unlimited seats, PDF text editing, and full admin control for $99/server/mo.", + "freeBody": "Our Open-Core licensing permits up to {{freeTierLimit}} users for free per server. To scale uninterrupted and get early access to our new PDF text editing tool, we recommend the Stirling Server plan - full editing and unlimited seats for $99/server/mo." + }, + "desktopInstall": { + "title": "Download", + "titleWithOs": "Download for {{osLabel}}", + "body": "Stirling works best as a desktop app. You can use it offline, access documents faster, and make edits locally on your computer." + }, + "planOverview": { + "adminTitle": "Admin Overview", + "userTitle": "Plan Overview", + "adminBodyLoginEnabled": "As an admin, you can manage users, configure settings, and monitor server health. The first {{freeTierLimit}} people on your server get to use Stirling free of charge.", + "adminBodyLoginDisabled": "Once you enable login mode, you can manage users, configure settings, and monitor server health. The first {{freeTierLimit}} people on your server get to use Stirling free of charge.", + "userBody": "Invite teammates, assign roles, and keep your documents organized in one secure workspace. Enable login mode whenever you're ready to grow beyond solo use." + }, + "securityCheck": { + "message": "The application has undergone significant changes recently. Your server admin's attention may be required. Please confirm your role to continue." + } }, "adminOnboarding": { "welcome": "Welcome to the Admin Tour! Let's explore the powerful enterprise features and settings available to system administrators.", @@ -5174,7 +5211,10 @@ "role": "Role", "team": "Team", "status": "Status", - "actions": "Actions", + "actions": { + "label": "Actions", + "upgrade": "Upgrade" + }, "noMembersFound": "No members found", "active": "Active", "disabled": "Disabled", @@ -5286,7 +5326,8 @@ "slotsAvailable": "{{count}} user slot(s) available", "noSlotsAvailable": "No slots available", "currentUsage": "Currently using {{current}} of {{max}} user licences" - } + }, + "loginRequired": "Enable login mode first" }, "teams": { "title": "Teams", @@ -5440,6 +5481,12 @@ "api": "API Access", "priority": "Priority Support", "customPricing": "Custom Pricing" + }, + "licenseWarning": { + "title": "Free self-hosted limit reached", + "body": "You have {{total}} users but the free tier only supports {{limit}} per server. Upgrade to keep Stirling PDF running smoothly.", + "overLimit": "more than {{limit}}", + "cta": "See plans" } }, "subscription": { @@ -5481,7 +5528,11 @@ "title": "Upgrade to Server Plan", "message": "Get the most out of Stirling PDF with unlimited users and advanced features", "upgradeButton": "Upgrade Now", - "dismiss": "Dismiss banner" + "dismiss": "Dismiss banner", + "attentionTitle": "This server needs admin attention", + "attentionBody": "Your admin needs to sign in to see more info. Please contact them immediately.", + "attentionBodyAdmin": "Review the license requirements to keep this server compliant.", + "seeInfo": "See info" }, "payment": { "preparing": "Preparing your checkout...", diff --git a/frontend/src/core/components/AppProviders.tsx b/frontend/src/core/components/AppProviders.tsx index 96c44bfa2..3d313cd68 100644 --- a/frontend/src/core/components/AppProviders.tsx +++ b/frontend/src/core/components/AppProviders.tsx @@ -17,6 +17,7 @@ import { TourOrchestrationProvider } from "@app/contexts/TourOrchestrationContex import { AdminTourOrchestrationProvider } from "@app/contexts/AdminTourOrchestrationContext"; import { PageEditorProvider } from "@app/contexts/PageEditorContext"; import { BannerProvider } from "@app/contexts/BannerContext"; +import { CookieConsentProvider } from "@app/contexts/CookieConsentContext"; import ErrorBoundary from "@app/components/shared/ErrorBoundary"; import { useScarfTracking } from "@app/hooks/useScarfTracking"; import { useAppInitialization } from "@app/hooks/useAppInitialization"; @@ -57,35 +58,37 @@ export function AppProviders({ children, appConfigRetryOptions, appConfigProvide retryOptions={appConfigRetryOptions} {...appConfigProviderProps} > - - - - - - - - - - - - - - - - {children} - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + {children} + + + + + + + + + + + + + + diff --git a/frontend/src/core/components/onboarding/InitialOnboardingModal/InitialOnboardingModal.module.css b/frontend/src/core/components/onboarding/InitialOnboardingModal/InitialOnboardingModal.module.css new file mode 100644 index 000000000..63a9e7977 --- /dev/null +++ b/frontend/src/core/components/onboarding/InitialOnboardingModal/InitialOnboardingModal.module.css @@ -0,0 +1,277 @@ +.heroWrapper { + position: relative; + width: 100%; + height: 220px; + overflow: hidden; +} + +.heroLogo { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + animation: heroLogoEnter 0.25s ease forwards; + z-index: 20; +} + +.heroLogoCircle { + width: 96px; + height: 96px; + border-radius: 50%; + background: #ffffff; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 16px 32px rgba(15, 23, 42, 0.18); + animation: heroLogoScale 0.25s ease forwards; +} + +.heroLogoCircle img { + width: 60px; + height: 60px; + animation: heroLogoRotate 0.25s ease forwards; + transform-origin: center; +} + +.standaloneIcon { + width: 96px; + height: 96px; + object-fit: contain; + animation: heroLogoScale 0.25s ease forwards; +} + +.securitySlideContent { + display: flex; + justify-content: center; + margin-top: 12px; +} + +.securityCard { + width: 100%; + max-width: 480px; + background: var(--bg-surface, #ffffff); + border-radius: 16px; + padding: 20px; + box-shadow: 0 12px 32px rgba(15, 23, 42, 0.12); + display: flex; + flex-direction: column; + gap: 16px; +} + +.securityAlertRow { + display: flex; + align-items: flex-start; + gap: 10px; + font-weight: 500; + font-size: 15px; + line-height: 1.5; + color: var(--onboarding-body, #1f2937); +} + +.heroIconsContainer { + display: flex; + gap: 32px; + align-items: flex-start; + justify-content: center; + animation: heroLogoEnter 0.25s ease forwards; + position: relative; + top: 1rem; +} + +.iconWrapper { + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; +} + +.iconButton { + background: none; + border: none; + padding: 0; + cursor: pointer; + transition: transform 0.2s ease, opacity 0.2s ease; + display: flex; + align-items: center; + justify-content: center; + position: relative; + border-radius: 12px; + padding: 4px; + outline: none; +} + +.iconButton:focus { + outline: none; +} + +.iconButton:focus-visible { + outline: none; +} + +.iconButton:hover { + transform: scale(1.05); + opacity: 0.9; +} + +.iconButton:active { + transform: scale(0.95); +} + +.iconButtonSelected { + background: rgba(255, 255, 255, 0.1); + box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.4); +} + +.downloadIcon { + width: 96px; + height: 96px; + object-fit: contain; + animation: heroLogoScale 0.25s ease forwards; +} + +.iconLabel { + font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; + font-size: 14px; + font-weight: 500; + color: rgba(255, 255, 255, 0.9); + text-align: center; + animation: heroLogoEnter 0.25s ease forwards; +} + +.title { + text-align: center; + opacity: 0; + transform: translateX(24px); + animation: bodySlideIn 0.25s ease forwards; +} + +.bodyCopy { + opacity: 0; + transform: translateX(24px); + animation: bodySlideIn 0.25s ease forwards; +} + +@keyframes heroLogoEnter { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes heroLogoScale { + from { + transform: scale(0.6); + } + to { + transform: scale(1); + } +} + +@keyframes heroLogoRotate { + from { + transform: rotate(-90deg) scale(0.9); + } + to { + transform: rotate(0deg) scale(1); + } +} + +@keyframes bodySlideIn { + from { + opacity: 0; + transform: translateX(24px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +/* Dev overlay styles */ +.devOverlay { + position: absolute; + bottom: 8px; + left: 10px; + display: flex; + gap: 6px; + z-index: 10; +} + +.devButton { + font-size: 10px; + padding: 2px 6px; + border-radius: 4px; + cursor: pointer; + opacity: 0.8; + border: 1px solid rgba(255, 255, 255, 0.4); + background: rgba(0, 0, 0, 0.35); + color: #fff; + box-shadow: none; +} + +.devButtonActive { + opacity: 1; + border: 1px solid rgba(255, 255, 255, 0.9); + background: rgba(255, 255, 255, 0.9); + color: #1F2933; + box-shadow: 0 0 8px rgba(255, 255, 255, 0.7); +} + +/* Modal content styles */ +.modalContent { + background: var(--bg-surface); + position: relative; +} + +.modalBody { + padding: 24px; +} + +/* Title styles */ +.titleText { + font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; + font-weight: 600; + font-size: 22px; + color: var(--onboarding-title); +} + +/* Body text styles */ +.bodyText { + font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; + font-size: 16px; + color: var(--onboarding-body); + line-height: 1.5; +} + +.bodyCopyInner { + color: inherit; +} + +/* Button margin */ +.buttonContainer { + margin-top: 8px; +} + +/* Welcome slide V2 badge */ +.welcomeTitleContainer { + display: flex; + align-items: center; + justify-content: center; + gap: 12px; +} + +.v2Badge { + background: #DBEFFF; + color: #2A4BFF; + padding: 4px 12px; + border-radius: 6px; + font-size: 14px; + font-weight: 600; +} + +/* Icon styles */ +.heroIcon { + color: #000000; +} diff --git a/frontend/src/core/components/onboarding/InitialOnboardingModal/flowResolver.ts b/frontend/src/core/components/onboarding/InitialOnboardingModal/flowResolver.ts new file mode 100644 index 000000000..ec742c143 --- /dev/null +++ b/frontend/src/core/components/onboarding/InitialOnboardingModal/flowResolver.ts @@ -0,0 +1,33 @@ +import { FLOW_SEQUENCES, type SlideId } from '@app/components/onboarding/onboardingFlowConfig'; + +export type FlowType = 'login-admin' | 'login-user' | 'no-login' | 'no-login-admin'; + +export interface FlowConfig { + type: FlowType; + ids: SlideId[]; +} + +export function resolveFlow(enableLogin: boolean, isAdmin: boolean, selfReportedAdmin: boolean): FlowConfig { + if (!enableLogin) { + return selfReportedAdmin + ? { + type: 'no-login-admin', + ids: [...FLOW_SEQUENCES.noLoginBase, ...FLOW_SEQUENCES.noLoginAdmin], + } + : { + type: 'no-login', + ids: FLOW_SEQUENCES.noLoginBase, + }; + } + + return isAdmin + ? { + type: 'login-admin', + ids: FLOW_SEQUENCES.loginAdmin, + } + : { + type: 'login-user', + ids: FLOW_SEQUENCES.loginUser, + }; +} + diff --git a/frontend/src/core/components/onboarding/InitialOnboardingModal/index.tsx b/frontend/src/core/components/onboarding/InitialOnboardingModal/index.tsx new file mode 100644 index 000000000..0ab33cfed --- /dev/null +++ b/frontend/src/core/components/onboarding/InitialOnboardingModal/index.tsx @@ -0,0 +1,119 @@ +import React from 'react'; +import { Modal, Stack } from '@mantine/core'; +import DiamondOutlinedIcon from '@mui/icons-material/DiamondOutlined'; +import LocalIcon from '@app/components/shared/LocalIcon'; +import AnimatedSlideBackground from '@app/components/onboarding/slides/AnimatedSlideBackground'; +import OnboardingStepper from '@app/components/onboarding/OnboardingStepper'; +import { renderButtons } from '@app/components/onboarding/InitialOnboardingModal/renderButtons'; +import styles from '@app/components/onboarding/InitialOnboardingModal/InitialOnboardingModal.module.css'; +import type { InitialOnboardingModalProps } from '@app/components/onboarding/InitialOnboardingModal/types'; +import { useInitialOnboardingState } from '@app/components/onboarding/InitialOnboardingModal/useInitialOnboardingState'; +import { BASE_PATH } from '@app/constants/app'; +import { Z_INDEX_OVER_FULLSCREEN_SURFACE } from '@app/styles/zIndex'; + +export default function InitialOnboardingModal(props: InitialOnboardingModalProps) { + const flow = useInitialOnboardingState(props); + + if (!flow) { + return null; + } + + const { + state, + totalSteps, + currentSlide, + slideDefinition, + licenseNotice, + flowState, + closeAndMarkSeen, + handleButtonAction, + } = flow; + + const renderHero = () => { + if (slideDefinition.hero.type === 'dual-icon') { + return ( +
+
+ Stirling icon +
+
+ ); + } + + return ( +
+ {slideDefinition.hero.type === 'rocket' && ( + + )} + {slideDefinition.hero.type === 'shield' && ( + + )} + {slideDefinition.hero.type === 'diamond' && } + {slideDefinition.hero.type === 'logo' && ( + Stirling logo + )} +
+ ); + }; + + return ( + + +
+ +
+ {renderHero()} +
+
+ +
+ +
+ {currentSlide.title} +
+ +
+
+ {currentSlide.body} +
+ +
+ + + +
+ {renderButtons({ + slideDefinition, + licenseNotice, + flowState, + onAction: handleButtonAction, + })} +
+
+
+
+
+ ); +} + diff --git a/frontend/src/core/components/onboarding/InitialOnboardingModal/renderButtons.tsx b/frontend/src/core/components/onboarding/InitialOnboardingModal/renderButtons.tsx new file mode 100644 index 000000000..4325d9b2f --- /dev/null +++ b/frontend/src/core/components/onboarding/InitialOnboardingModal/renderButtons.tsx @@ -0,0 +1,109 @@ +import React from 'react'; +import { Button, Group, ActionIcon } from '@mantine/core'; +import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; +import { useTranslation } from 'react-i18next'; +import { ButtonDefinition, type FlowState } from '@app/components/onboarding/onboardingFlowConfig'; +import type { LicenseNotice } from '@app/types/types'; +import type { ButtonAction } from '@app/components/onboarding/onboardingFlowConfig'; + +interface RenderButtonsProps { + slideDefinition: { + buttons: ButtonDefinition[]; + id: string; + }; + licenseNotice: LicenseNotice; + flowState: FlowState; + onAction: (action: ButtonAction) => void; +} + +export function renderButtons({ slideDefinition, licenseNotice, flowState, onAction }: RenderButtonsProps) { + const { t } = useTranslation(); + const leftButtons = slideDefinition.buttons.filter((btn) => btn.group === 'left'); + const rightButtons = slideDefinition.buttons.filter((btn) => btn.group === 'right'); + + const buttonStyles = (variant: ButtonDefinition['variant']) => + variant === 'primary' + ? { + root: { + background: 'var(--onboarding-primary-button-bg)', + color: 'var(--onboarding-primary-button-text)', + }, + } + : { + root: { + background: 'var(--onboarding-secondary-button-bg)', + border: '1px solid var(--onboarding-secondary-button-border)', + color: 'var(--onboarding-secondary-button-text)', + }, + }; + + const resolveButtonLabel = (button: ButtonDefinition) => { + // Special case: override "See Plans" with "Upgrade now" when over limit + if ( + button.type === 'button' && + slideDefinition.id === 'server-license' && + button.action === 'see-plans' && + licenseNotice.isOverLimit + ) { + return t('onboarding.serverLicense.upgrade', 'Upgrade now →'); + } + + // Translate the label (it's a translation key) + const label = button.label ?? ''; + if (!label) return ''; + + // Extract fallback text from translation key (e.g., 'onboarding.buttons.next' -> 'Next') + const fallback = label.split('.').pop() || label; + return t(label, fallback); + }; + + const renderButton = (button: ButtonDefinition) => { + const disabled = button.disabledWhen?.(flowState) ?? false; + + if (button.type === 'icon') { + return ( + onAction(button.action)} + radius="md" + size={40} + disabled={disabled} + styles={{ + root: { + background: 'var(--onboarding-secondary-button-bg)', + border: '1px solid var(--onboarding-secondary-button-border)', + color: 'var(--onboarding-secondary-button-text)', + }, + }} + > + {button.icon === 'chevron-left' && } + + ); + } + + const variant = button.variant ?? 'secondary'; + const label = resolveButtonLabel(button); + + return ( + + ); + }; + + if (leftButtons.length === 0) { + return {rightButtons.map(renderButton)}; + } + + if (rightButtons.length === 0) { + return {leftButtons.map(renderButton)}; + } + + return ( + + {leftButtons.map(renderButton)} + {rightButtons.map(renderButton)} + + ); +} + diff --git a/frontend/src/core/components/onboarding/InitialOnboardingModal/types.ts b/frontend/src/core/components/onboarding/InitialOnboardingModal/types.ts new file mode 100644 index 000000000..d8bc96a1c --- /dev/null +++ b/frontend/src/core/components/onboarding/InitialOnboardingModal/types.ts @@ -0,0 +1,21 @@ +import type { LicenseNotice } from '@app/types/types'; + +export interface InitialOnboardingModalProps { + opened: boolean; + onClose: () => void; + onRequestServerLicense?: (options?: { deferUntilTourComplete?: boolean; selfReportedAdmin?: boolean }) => void; + onLicenseNoticeUpdate?: (licenseNotice: LicenseNotice) => void; +} + +export interface OnboardingState { + step: number; + selectedRole: 'admin' | 'user' | null; + selfReportedAdmin: boolean; +} + +export const DEFAULT_STATE: OnboardingState = { + step: 0, + selectedRole: null, + selfReportedAdmin: false, +}; + diff --git a/frontend/src/core/components/onboarding/InitialOnboardingModal/useInitialOnboardingState.ts b/frontend/src/core/components/onboarding/InitialOnboardingModal/useInitialOnboardingState.ts new file mode 100644 index 000000000..066a777cc --- /dev/null +++ b/frontend/src/core/components/onboarding/InitialOnboardingModal/useInitialOnboardingState.ts @@ -0,0 +1,381 @@ +import { useCallback, useEffect, useMemo, useState, useRef } from 'react'; +import { usePreferences } from '@app/contexts/PreferencesContext'; +import { useOnboarding } from '@app/contexts/OnboardingContext'; +import { useOs } from '@app/hooks/useOs'; +import { useNavigate } from 'react-router-dom'; +import { + SLIDE_DEFINITIONS, + type ButtonAction, + type FlowState, + type SlideId, +} from '@app/components/onboarding/onboardingFlowConfig'; +import type { LicenseNotice } from '@app/types/types'; +import { resolveFlow } from '@app/components/onboarding/InitialOnboardingModal/flowResolver'; +import { useServerExperience } from '@app/hooks/useServerExperience'; +import { DEFAULT_STATE, type InitialOnboardingModalProps, type OnboardingState } from '@app/components/onboarding/InitialOnboardingModal/types'; +import { DOWNLOAD_URLS } from '@app/constants/downloads'; + +interface UseInitialOnboardingStateResult { + state: OnboardingState; + totalSteps: number; + slideDefinition: (typeof SLIDE_DEFINITIONS)[SlideId]; + currentSlide: ReturnType<(typeof SLIDE_DEFINITIONS)[SlideId]['createSlide']>; + licenseNotice: LicenseNotice; + flowState: FlowState; + closeAndMarkSeen: () => void; + handleButtonAction: (action: ButtonAction) => void; +} + +export function useInitialOnboardingState({ + opened, + onClose, + onRequestServerLicense, + onLicenseNoticeUpdate, +}: InitialOnboardingModalProps): UseInitialOnboardingStateResult | null { + const { preferences, updatePreference } = usePreferences(); + const { startTour } = useOnboarding(); + const { + loginEnabled: loginEnabledFromServer, + configIsAdmin, + totalUsers: serverTotalUsers, + userCountResolved: serverUserCountResolved, + freeTierLimit, + hasPaidLicense, + scenarioKey, + setSelfReportedAdmin, + isNewServer, + } = useServerExperience(); + const osType = useOs(); + const navigate = useNavigate(); + const selectedDownloadUrlRef = useRef(''); + + const [state, setState] = useState(DEFAULT_STATE); + + const resetState = useCallback(() => { + setState(DEFAULT_STATE); + }, []); + + useEffect(() => { + if (!opened) { + resetState(); + } + }, [opened, resetState]); + + const handleRoleSelect = useCallback( + (role: 'admin' | 'user' | null) => { + const isAdminSelection = role === 'admin'; + setState((prev) => ({ + ...prev, + selectedRole: role, + selfReportedAdmin: isAdminSelection, + })); + + if (typeof window !== 'undefined') { + if (isAdminSelection) { + window.localStorage.setItem('stirling-self-reported-admin', 'true'); + } else { + window.localStorage.removeItem('stirling-self-reported-admin'); + } + } + + setSelfReportedAdmin(isAdminSelection); + }, + [setSelfReportedAdmin], + ); + + const closeAndMarkSeen = useCallback(() => { + if (!preferences.hasSeenIntroOnboarding) { + updatePreference('hasSeenIntroOnboarding', true); + } + onClose(); + }, [onClose, preferences.hasSeenIntroOnboarding, updatePreference]); + + const isAdmin = configIsAdmin; + const enableLogin = loginEnabledFromServer; + + const effectiveEnableLogin = enableLogin; + const effectiveIsAdmin = isAdmin; + const shouldAssumeAdminForNewServer = Boolean(isNewServer) && !effectiveEnableLogin; + + useEffect(() => { + if (shouldAssumeAdminForNewServer && !state.selfReportedAdmin) { + handleRoleSelect('admin'); + } + }, [handleRoleSelect, shouldAssumeAdminForNewServer, state.selfReportedAdmin]); + + const shouldUseServerCount = + (effectiveEnableLogin && effectiveIsAdmin) || !effectiveEnableLogin; + const licenseUserCountFromServer = + shouldUseServerCount && serverUserCountResolved ? serverTotalUsers : null; + + const effectiveLicenseUserCount = licenseUserCountFromServer ?? null; + + const os = useMemo(() => { + switch (osType) { + case 'windows': + return { label: 'Windows', url: DOWNLOAD_URLS.WINDOWS }; + case 'mac-apple': + return { label: 'Mac (Apple Silicon)', url: DOWNLOAD_URLS.MAC_APPLE_SILICON }; + case 'mac-intel': + return { label: 'Mac (Intel)', url: DOWNLOAD_URLS.MAC_INTEL }; + case 'linux-x64': + case 'linux-arm64': + return { label: 'Linux', url: DOWNLOAD_URLS.LINUX_DOCS }; + default: + return { label: '', url: '' }; + } + }, [osType]); + + const osOptions = useMemo(() => { + const options = [ + { label: 'Windows', url: DOWNLOAD_URLS.WINDOWS, value: 'windows' }, + { label: 'Mac (Apple Silicon)', url: DOWNLOAD_URLS.MAC_APPLE_SILICON, value: 'mac-apple' }, + { label: 'Mac (Intel)', url: DOWNLOAD_URLS.MAC_INTEL, value: 'mac-intel' }, + { label: 'Linux', url: DOWNLOAD_URLS.LINUX_DOCS, value: 'linux' }, + ]; + return options.filter(opt => opt.url); + }, []); + + const resolvedFlow = useMemo( + () => resolveFlow(effectiveEnableLogin, effectiveIsAdmin, state.selfReportedAdmin), + [effectiveEnableLogin, effectiveIsAdmin, state.selfReportedAdmin], + ); + const shouldSkipSecurityCheck = shouldAssumeAdminForNewServer; + const flowSlideIds = useMemo( + () => + shouldSkipSecurityCheck + ? resolvedFlow.ids.filter((id) => id !== 'security-check') + : resolvedFlow.ids, + [resolvedFlow.ids, shouldSkipSecurityCheck], + ); + const flowType = resolvedFlow.type; + const totalSteps = flowSlideIds.length; + const maxIndex = Math.max(totalSteps - 1, 0); + + useEffect(() => { + if (state.step >= flowSlideIds.length) { + setState((prev) => ({ + ...prev, + step: Math.max(flowSlideIds.length - 1, 0), + })); + } + }, [flowSlideIds.length, state.step]); + + const currentSlideId = flowSlideIds[state.step] ?? flowSlideIds[flowSlideIds.length - 1]; + const slideDefinition = SLIDE_DEFINITIONS[currentSlideId]; + + if (!slideDefinition) { + return null; + } + + const scenarioProvidesInfo = + scenarioKey && scenarioKey !== 'unknown' && scenarioKey !== 'licensed'; + const scenarioIndicatesAdmin = scenarioProvidesInfo + ? scenarioKey!.includes('admin') + : state.selfReportedAdmin || effectiveIsAdmin; + const scenarioIndicatesOverLimit = scenarioProvidesInfo + ? scenarioKey!.includes('over-limit') + : effectiveLicenseUserCount != null && effectiveLicenseUserCount > freeTierLimit; + const scenarioRequiresLicense = + scenarioKey === 'licensed' ? false : scenarioKey === 'unknown' ? !hasPaidLicense : true; + + const shouldShowServerLicenseInfo = scenarioIndicatesAdmin && scenarioRequiresLicense; + + const licenseNotice = useMemo( + () => ({ + totalUsers: effectiveLicenseUserCount, + freeTierLimit, + isOverLimit: scenarioIndicatesOverLimit, + requiresLicense: shouldShowServerLicenseInfo, + }), + [ + effectiveLicenseUserCount, + freeTierLimit, + scenarioIndicatesOverLimit, + shouldShowServerLicenseInfo, + ], + ); + + const requestServerLicenseIfNeeded = useCallback( + (options?: { deferUntilTourComplete?: boolean; selfReportedAdmin?: boolean }) => { + if (!shouldShowServerLicenseInfo) { + return; + } + onRequestServerLicense?.(options); + }, + [onRequestServerLicense, shouldShowServerLicenseInfo], + ); + + useEffect(() => { + onLicenseNoticeUpdate?.(licenseNotice); + }, [licenseNotice, onLicenseNoticeUpdate]); + + // Initialize ref with default URL + useEffect(() => { + if (!selectedDownloadUrlRef.current && os.url) { + selectedDownloadUrlRef.current = os.url; + } + }, [os.url]); + + const handleDownloadUrlChange = useCallback((url: string) => { + selectedDownloadUrlRef.current = url; + }, []); + + const currentSlide = slideDefinition.createSlide({ + osLabel: os.label, + osUrl: os.url, + osOptions, + onDownloadUrlChange: handleDownloadUrlChange, + selectedRole: state.selectedRole, + onRoleSelect: handleRoleSelect, + licenseNotice, + loginEnabled: effectiveEnableLogin, + }); + + const goNext = useCallback(() => { + setState((prev) => ({ + ...prev, + step: Math.min(prev.step + 1, maxIndex), + })); + }, [maxIndex]); + + const goPrev = useCallback(() => { + setState((prev) => ({ + ...prev, + step: Math.max(prev.step - 1, 0), + })); + }, []); + + const launchTour = useCallback( + (mode: 'admin' | 'tools', options?: { closeOnboardingSlides?: boolean }) => { + if (options?.closeOnboardingSlides) { + closeAndMarkSeen(); + } + + startTour(mode, { + source: 'initial-onboarding-modal', + metadata: { + hasCompletedOnboarding: preferences.hasCompletedOnboarding, + toolPanelModePromptSeen: preferences.toolPanelModePromptSeen, + selfReportedAdmin: state.selfReportedAdmin, + }, + }); + }, + [closeAndMarkSeen, preferences.hasCompletedOnboarding, preferences.toolPanelModePromptSeen, startTour, state.selfReportedAdmin], + ); + + const handleButtonAction = useCallback( + (action: ButtonAction) => { + const currentSlideIdLocal = currentSlideId; + const shouldAutoLaunchLoginUserTour = + flowType === 'login-user' && currentSlideIdLocal === 'desktop-install'; + + switch (action) { + case 'next': + if (shouldAutoLaunchLoginUserTour) { + launchTour('tools', { closeOnboardingSlides: true }); + return; + } + goNext(); + return; + case 'prev': + goPrev(); + return; + case 'close': + closeAndMarkSeen(); + return; + case 'download-selected': { + const downloadUrl = selectedDownloadUrlRef.current || os.url || currentSlide.downloadUrl; + if (downloadUrl) { + window.open(downloadUrl, '_blank', 'noopener'); + } + if (shouldAutoLaunchLoginUserTour) { + launchTour('tools', { closeOnboardingSlides: true }); + return; + } + goNext(); + return; + } + case 'complete-close': + updatePreference('hasCompletedOnboarding', true); + closeAndMarkSeen(); + return; + case 'security-next': + if (!state.selectedRole) { + return; + } + if (state.selectedRole === 'admin') { + goNext(); + } else { + launchTour('tools', { closeOnboardingSlides: true }); + } + return; + case 'launch-admin': + requestServerLicenseIfNeeded({ + deferUntilTourComplete: true, + selfReportedAdmin: state.selfReportedAdmin || effectiveIsAdmin, + }); + launchTour('admin', { closeOnboardingSlides: true }); + return; + case 'launch-tools': + launchTour('tools', { closeOnboardingSlides: true }); + return; + case 'launch-auto': { + const launchMode = state.selfReportedAdmin || effectiveIsAdmin ? 'admin' : 'tools'; + if (launchMode === 'admin') { + requestServerLicenseIfNeeded({ + deferUntilTourComplete: true, + selfReportedAdmin: state.selfReportedAdmin || effectiveIsAdmin, + }); + } + launchTour(launchMode, { closeOnboardingSlides: true }); + return; + } + case 'skip-to-license': + updatePreference('hasCompletedOnboarding', true); + requestServerLicenseIfNeeded({ + deferUntilTourComplete: false, + selfReportedAdmin: state.selfReportedAdmin || effectiveIsAdmin, + }); + closeAndMarkSeen(); + return; + case 'see-plans': + closeAndMarkSeen(); + navigate('/settings/adminPlan'); + return; + default: + return; + } + }, + [ + closeAndMarkSeen, + currentSlide, + effectiveIsAdmin, + flowType, + goNext, + goPrev, + launchTour, + navigate, + requestServerLicenseIfNeeded, + onRequestServerLicense, + os.url, + state.selectedRole, + state.selfReportedAdmin, + updatePreference, + ], + ); + + const flowState: FlowState = { selectedRole: state.selectedRole }; + + return { + state, + totalSteps, + slideDefinition, + currentSlide, + licenseNotice, + flowState, + closeAndMarkSeen, + handleButtonAction, + }; +} + diff --git a/frontend/src/core/components/onboarding/OnboardingStepper.tsx b/frontend/src/core/components/onboarding/OnboardingStepper.tsx new file mode 100644 index 000000000..ec6767d8a --- /dev/null +++ b/frontend/src/core/components/onboarding/OnboardingStepper.tsx @@ -0,0 +1,52 @@ +import React from 'react'; + +interface OnboardingStepperProps { + totalSteps: number; + activeStep: number; // 0-indexed + className?: string; +} + +/** + * Renders a progress indicator where the active step is a pill and others are dots. + * Colors come from theme.css variables. + */ +export function OnboardingStepper({ totalSteps, activeStep, className }: OnboardingStepperProps) { + const items = Array.from({ length: totalSteps }, (_, index) => index); + + return ( +
+ {items.map((index) => { + const isActive = index === activeStep; + const baseStyles: React.CSSProperties = { + background: isActive + ? 'var(--onboarding-step-active)' + : 'var(--onboarding-step-inactive)', + }; + + return ( +
+ ); + })} +
+ ); +} + +export default OnboardingStepper; + + diff --git a/frontend/src/core/components/onboarding/OnboardingTour.tsx b/frontend/src/core/components/onboarding/OnboardingTour.tsx index e06fa41d5..b044fd3e5 100644 --- a/frontend/src/core/components/onboarding/OnboardingTour.tsx +++ b/frontend/src/core/components/onboarding/OnboardingTour.tsx @@ -1,105 +1,29 @@ -import React, { useMemo } from "react"; -import { TourProvider, useTour, type StepType } from '@reactour/tour'; -import { useOnboarding } from '@app/contexts/OnboardingContext'; +import React, { useEffect, useMemo } from "react"; +import { TourProvider, type StepType } from '@reactour/tour'; import { useTranslation } from 'react-i18next'; import { CloseButton, ActionIcon } from '@mantine/core'; +import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; +import CheckIcon from '@mui/icons-material/Check'; +import InitialOnboardingModal from '@app/components/onboarding/InitialOnboardingModal'; +import ServerLicenseModal from '@app/components/onboarding/ServerLicenseModal'; +import '@app/components/onboarding/OnboardingTour.css'; +import ToolPanelModePrompt from '@app/components/tools/ToolPanelModePrompt'; import { useFilesModalContext } from '@app/contexts/FilesModalContext'; import { useTourOrchestration } from '@app/contexts/TourOrchestrationContext'; import { useAdminTourOrchestration } from '@app/contexts/AdminTourOrchestrationContext'; -import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; +import { useOnboardingFlow } from '@app/components/onboarding/hooks/useOnboardingFlow'; +import { createUserStepsConfig } from '@app/components/onboarding/userStepsConfig'; +import { createAdminStepsConfig } from '@app/components/onboarding/adminStepsConfig'; +import { removeAllGlows } from '@app/components/onboarding/tourGlow'; +import TourContent from '@app/components/onboarding/TourContent'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; -import CheckIcon from '@mui/icons-material/Check'; -import TourWelcomeModal from '@app/components/onboarding/TourWelcomeModal'; import '@app/components/onboarding/OnboardingTour.css'; import i18n from "@app/i18n"; -// Enum case order defines order steps will appear -enum TourStep { - ALL_TOOLS, - SELECT_CROP_TOOL, - TOOL_INTERFACE, - FILES_BUTTON, - FILE_SOURCES, - WORKBENCH, - VIEW_SWITCHER, - VIEWER, - PAGE_EDITOR, - ACTIVE_FILES, - FILE_CHECKBOX, - SELECT_CONTROLS, - CROP_SETTINGS, - RUN_BUTTON, - RESULTS, - FILE_REPLACEMENT, - PIN_BUTTON, - WRAP_UP, -} - -enum AdminTourStep { - WELCOME, - CONFIG_BUTTON, - SETTINGS_OVERVIEW, - TEAMS_AND_USERS, - SYSTEM_CUSTOMIZATION, - DATABASE_SECTION, - CONNECTIONS_SECTION, - ADMIN_TOOLS, - WRAP_UP, -} - -function TourContent() { - const { isOpen } = useOnboarding(); - const { setIsOpen, setCurrentStep } = useTour(); - const previousIsOpenRef = React.useRef(isOpen); - - // Sync tour open state with context and reset to step 0 when reopening - React.useEffect(() => { - const wasClosedNowOpen = !previousIsOpenRef.current && isOpen; - previousIsOpenRef.current = isOpen; - - if (wasClosedNowOpen) { - // Tour is being opened (Help button pressed), reset to first step - setCurrentStep(0); - } - setIsOpen(isOpen); - }, [isOpen, setIsOpen, setCurrentStep]); - - return null; -} - export default function OnboardingTour() { const { t } = useTranslation(); - const { completeTour, showWelcomeModal, setShowWelcomeModal, startTour, tourType, isOpen } = useOnboarding(); + const flow = useOnboardingFlow(); const { openFilesModal, closeFilesModal } = useFilesModalContext(); - const isRTL = typeof document !== 'undefined' ? document.documentElement.dir === 'rtl' : false; - - // Helper to add glow to multiple elements - const addGlowToElements = (selectors: string[]) => { - selectors.forEach(selector => { - const element = document.querySelector(selector); - if (element) { - if (selector === '[data-tour="settings-content-area"]') { - element.classList.add('tour-content-glow'); - } else { - element.classList.add('tour-nav-glow'); - } - } - }); - }; - - // Helper to remove all glows - const removeAllGlows = () => { - document.querySelectorAll('.tour-content-glow').forEach(el => el.classList.remove('tour-content-glow')); - document.querySelectorAll('.tour-nav-glow').forEach(el => el.classList.remove('tour-nav-glow')); - }; - - // Cleanup glows when tour closes - React.useEffect(() => { - if (!isOpen) { - removeAllGlows(); - } - return () => removeAllGlows(); - }, [isOpen]); const { saveWorkbenchState, restoreWorkbenchState, @@ -122,258 +46,78 @@ export default function OnboardingTour() { scrollNavToSection, } = useAdminTourOrchestration(); - // Define steps as object keyed by enum - TypeScript ensures all keys are present - const stepsConfig: Record = useMemo(() => ({ - [TourStep.ALL_TOOLS]: { - selector: '[data-tour="tool-panel"]', - content: t('onboarding.allTools', 'This is the Tools panel, where you can browse and select from all available PDF tools.'), - position: 'center', - padding: 0, - action: () => { - saveWorkbenchState(); - closeFilesModal(); - backToAllTools(); - }, - }, - [TourStep.SELECT_CROP_TOOL]: { - selector: '[data-tour="tool-button-crop"]', - content: t('onboarding.selectCropTool', "Let's select the Crop tool to demonstrate how to use one of the tools."), - position: 'right', - padding: 0, - actionAfter: () => selectCropTool(), - }, - [TourStep.TOOL_INTERFACE]: { - selector: '[data-tour="tool-panel"]', - content: t('onboarding.toolInterface', "This is the Crop tool interface. As you can see, there's not much there because we haven't added any PDF files to work with yet."), - position: 'center', - padding: 0, - }, - [TourStep.FILES_BUTTON]: { - selector: '[data-tour="files-button"]', - content: t('onboarding.filesButton', "The Files button on the Quick Access bar allows you to upload PDFs to use the tools on."), - position: 'right', - padding: 10, - action: () => openFilesModal(), - }, - [TourStep.FILE_SOURCES]: { - selector: '[data-tour="file-sources"]', - content: t('onboarding.fileSources', "You can upload new files or access recent files from here. For the tour, we'll just use a sample file."), - position: 'right', - padding: 0, - actionAfter: () => { - loadSampleFile(); - closeFilesModal(); - } - }, - [TourStep.WORKBENCH]: { - selector: '[data-tour="workbench"]', - content: t('onboarding.workbench', 'This is the Workbench - the main area where you view and edit your PDFs.'), - position: 'center', - padding: 0, - }, - [TourStep.VIEW_SWITCHER]: { - selector: '[data-tour="view-switcher"]', - content: t('onboarding.viewSwitcher', 'Use these controls to select how you want to view your PDFs.'), - position: 'bottom', - padding: 0, - }, - [TourStep.VIEWER]: { - selector: '[data-tour="workbench"]', - content: t('onboarding.viewer', "The Viewer lets you read and annotate your PDFs."), - position: 'center', - padding: 0, - action: () => switchToViewer(), - }, - [TourStep.PAGE_EDITOR]: { - selector: '[data-tour="workbench"]', - content: t('onboarding.pageEditor', "The Page Editor allows you to do various operations on the pages within your PDFs, such as reordering, rotating and deleting."), - position: 'center', - padding: 0, - action: () => switchToPageEditor(), - }, - [TourStep.ACTIVE_FILES]: { - selector: '[data-tour="workbench"]', - content: t('onboarding.activeFiles', "The Active Files view shows all of the PDFs you have loaded into the tool, and allows you to select which ones to process."), - position: 'center', - padding: 0, - action: () => switchToActiveFiles(), - }, - [TourStep.FILE_CHECKBOX]: { - selector: '[data-tour="file-card-checkbox"]', - content: t('onboarding.fileCheckbox', "Clicking one of the files selects it for processing. You can select multiple files for batch operations."), - position: 'top', - padding: 10, - }, - [TourStep.SELECT_CONTROLS]: { - selector: '[data-tour="right-rail-controls"]', - highlightedSelectors: ['[data-tour="right-rail-controls"]', '[data-tour="right-rail-settings"]'], - content: t('onboarding.selectControls', "The Right Rail contains buttons to quickly select/deselect all of your active PDFs, along with buttons to change the app's theme or language."), - position: 'left', - padding: 5, - action: () => selectFirstFile(), - }, - [TourStep.CROP_SETTINGS]: { - selector: '[data-tour="crop-settings"]', - content: t('onboarding.cropSettings', "Now that we've selected the file we want crop, we can configure the Crop tool to choose the area that we want to crop the PDF to."), - position: 'left', - padding: 10, - action: () => modifyCropSettings(), - }, - [TourStep.RUN_BUTTON]: { - selector: '[data-tour="run-button"]', - content: t('onboarding.runButton', "Once the tool has been configured, this button allows you to run the tool on all the selected PDFs."), - position: 'top', - padding: 10, - actionAfter: () => executeTool(), - }, - [TourStep.RESULTS]: { - selector: '[data-tour="tool-panel"]', - content: t('onboarding.results', "After the tool has finished running, the Review step will show a preview of the results in this panel, and allow you to undo the operation or download the file. "), - position: 'center', - padding: 0, - }, - [TourStep.FILE_REPLACEMENT]: { - selector: '[data-tour="file-card-checkbox"]', - content: t('onboarding.fileReplacement', "The modified file will replace the original file in the Workbench automatically, allowing you to easily run it through more tools."), - position: 'left', - padding: 10, - }, - [TourStep.PIN_BUTTON]: { - selector: '[data-tour="file-card-pin"]', - content: t('onboarding.pinButton', "You can use the Pin button if you'd rather your files stay active after running tools on them."), - position: 'left', - padding: 10, - action: () => pinFile(), - }, - [TourStep.WRAP_UP]: { - selector: '[data-tour="help-button"]', - content: t('onboarding.wrapUp', "You're all set! You've learnt about the main areas of the app and how to use them. Click the Help button whenever you like to see this tour again."), - position: 'right', - padding: 10, - }, - }), [t]); + const isRTL = typeof document !== 'undefined' ? document.documentElement.dir === 'rtl' : false; - // Define admin tour steps - const adminStepsConfig: Record = useMemo(() => ({ - [AdminTourStep.WELCOME]: { - selector: '[data-tour="config-button"]', - content: t('adminOnboarding.welcome', "Welcome to the Admin Tour! Let's explore the powerful enterprise features and settings available to system administrators."), - position: 'right', - padding: 10, - action: () => { - saveAdminState(); - }, - }, - [AdminTourStep.CONFIG_BUTTON]: { - selector: '[data-tour="config-button"]', - content: t('adminOnboarding.configButton', "Click the Config button to access all system settings and administrative controls."), - position: 'right', - padding: 10, - actionAfter: () => { - openConfigModal(); - }, - }, - [AdminTourStep.SETTINGS_OVERVIEW]: { - selector: '.modal-nav', - content: t('adminOnboarding.settingsOverview', "This is the Settings Panel. Admin settings are organised by category for easy navigation."), - position: 'right', - padding: 0, - action: () => { - removeAllGlows(); - }, - }, - [AdminTourStep.TEAMS_AND_USERS]: { - selector: '[data-tour="admin-people-nav"]', - highlightedSelectors: ['[data-tour="admin-people-nav"]', '[data-tour="admin-teams-nav"]', '[data-tour="settings-content-area"]'], - content: t('adminOnboarding.teamsAndUsers', "Manage Teams and individual users here. You can invite new users via email, shareable links, or create custom accounts for them yourself."), - position: 'right', - padding: 10, - action: () => { - removeAllGlows(); - navigateToSection('people'); - setTimeout(() => { - addGlowToElements(['[data-tour="admin-people-nav"]', '[data-tour="admin-teams-nav"]', '[data-tour="settings-content-area"]']); - }, 100); - }, - }, - [AdminTourStep.SYSTEM_CUSTOMIZATION]: { - selector: '[data-tour="admin-adminGeneral-nav"]', - highlightedSelectors: ['[data-tour="admin-adminGeneral-nav"]', '[data-tour="admin-adminFeatures-nav"]', '[data-tour="admin-adminEndpoints-nav"]', '[data-tour="settings-content-area"]'], - content: t('adminOnboarding.systemCustomization', "We have extensive ways to customise the UI: System Settings let you change the app name and languages, Features allows server certificate management, and Endpoints lets you enable or disable specific tools for your users."), - position: 'right', - padding: 10, - action: () => { - removeAllGlows(); - navigateToSection('adminGeneral'); - setTimeout(() => { - addGlowToElements(['[data-tour="admin-adminGeneral-nav"]', '[data-tour="admin-adminFeatures-nav"]', '[data-tour="admin-adminEndpoints-nav"]', '[data-tour="settings-content-area"]']); - }, 100); - }, - }, - [AdminTourStep.DATABASE_SECTION]: { - selector: '[data-tour="admin-adminDatabase-nav"]', - highlightedSelectors: ['[data-tour="admin-adminDatabase-nav"]', '[data-tour="settings-content-area"]'], - content: t('adminOnboarding.databaseSection', "For advanced production environments, we have settings to allow external database hookups so you can integrate with your existing infrastructure."), - position: 'right', - padding: 10, - action: () => { - removeAllGlows(); - navigateToSection('adminDatabase'); - setTimeout(() => { - addGlowToElements(['[data-tour="admin-adminDatabase-nav"]', '[data-tour="settings-content-area"]']); - }, 100); - }, - }, - [AdminTourStep.CONNECTIONS_SECTION]: { - selector: '[data-tour="admin-adminConnections-nav"]', - highlightedSelectors: ['[data-tour="admin-adminConnections-nav"]', '[data-tour="settings-content-area"]'], - content: t('adminOnboarding.connectionsSection', "The Connections section supports various login methods including custom SSO and SAML providers like Google and GitHub, plus email integrations for notifications and communications."), - position: 'right', - padding: 10, - action: () => { - removeAllGlows(); - navigateToSection('adminConnections'); - setTimeout(() => { - addGlowToElements(['[data-tour="admin-adminConnections-nav"]', '[data-tour="settings-content-area"]']); - }, 100); - }, - actionAfter: async () => { - // Scroll for the NEXT step before it shows - await scrollNavToSection('adminAudit'); - }, - }, - [AdminTourStep.ADMIN_TOOLS]: { - selector: '[data-tour="admin-adminAudit-nav"]', - highlightedSelectors: ['[data-tour="admin-adminAudit-nav"]', '[data-tour="admin-adminUsage-nav"]', '[data-tour="settings-content-area"]'], - content: t('adminOnboarding.adminTools', "Finally, we have advanced administration tools like Auditing to track system activity and Usage Analytics to monitor how your users interact with the platform."), - position: 'right', - padding: 10, - action: () => { - // Just navigate, scroll already happened in previous step - removeAllGlows(); - navigateToSection('adminAudit'); - setTimeout(() => { - addGlowToElements(['[data-tour="admin-adminAudit-nav"]', '[data-tour="admin-adminUsage-nav"]', '[data-tour="settings-content-area"]']); - }, 100); - }, - }, - [AdminTourStep.WRAP_UP]: { - selector: '[data-tour="help-button"]', - content: t('adminOnboarding.wrapUp', "That's the admin tour! You've seen the enterprise features that make Stirling PDF a powerful, customisable solution for organisations. Access this tour anytime from the Help menu."), - position: 'right', - padding: 10, - action: () => { - removeAllGlows(); - }, - }, - }), [t]); + useEffect(() => { + if (!flow.isTourOpen) { + removeAllGlows(); + } + return () => removeAllGlows(); + }, [flow.isTourOpen]); - // Select steps based on tour type - const steps = tourType === 'admin' - ? Object.values(adminStepsConfig) - : Object.values(stepsConfig); + const userStepsConfig = useMemo( + () => + createUserStepsConfig({ + t, + actions: { + saveWorkbenchState, + closeFilesModal, + backToAllTools, + selectCropTool, + loadSampleFile, + switchToViewer, + switchToPageEditor, + switchToActiveFiles, + selectFirstFile, + pinFile, + modifyCropSettings, + executeTool, + openFilesModal, + }, + }), + [ + t, + backToAllTools, + closeFilesModal, + executeTool, + loadSampleFile, + modifyCropSettings, + openFilesModal, + pinFile, + saveWorkbenchState, + selectCropTool, + selectFirstFile, + switchToActiveFiles, + switchToPageEditor, + switchToViewer, + ], + ); - const advanceTour = ({ setCurrentStep, currentStep, steps, setIsOpen }: { + const adminStepsConfig = useMemo( + () => + createAdminStepsConfig({ + t, + actions: { + saveAdminState, + openConfigModal, + navigateToSection, + scrollNavToSection, + }, + }), + [navigateToSection, openConfigModal, saveAdminState, scrollNavToSection, t], + ); + + const steps = useMemo(() => { + const config = flow.tourType === 'admin' ? adminStepsConfig : userStepsConfig; + return Object.values(config); + }, [adminStepsConfig, flow.tourType, userStepsConfig]); + + const advanceTour = ({ + setCurrentStep, + currentStep, + steps, + setIsOpen, + }: { setCurrentStep: (value: number | ((prev: number) => number)) => void; currentStep: number; steps?: StepType[]; @@ -381,12 +125,12 @@ export default function OnboardingTour() { }) => { if (steps && currentStep === steps.length - 1) { setIsOpen(false); - if (tourType === 'admin') { + if (flow.tourType === 'admin') { restoreAdminState(); } else { restoreWorkbenchState(); } - completeTour(); + flow.handleTourCompletion(); } else if (steps) { setCurrentStep((s) => (s === steps.length - 1 ? 0 : s + 1)); } @@ -394,34 +138,22 @@ export default function OnboardingTour() { const handleCloseTour = ({ setIsOpen }: { setIsOpen: (value: boolean) => void }) => { setIsOpen(false); - if (tourType === 'admin') { + if (flow.tourType === 'admin') { restoreAdminState(); } else { restoreWorkbenchState(); } - completeTour(); + flow.handleTourCompletion(); }; return ( <> - { - setShowWelcomeModal(false); - startTour(); - }} - onMaybeLater={() => { - setShowWelcomeModal(false); - }} - onDontShowAgain={() => { - setShowWelcomeModal(false); - completeTour(); - }} - /> + + { @@ -429,13 +161,10 @@ export default function OnboardingTour() { advanceTour(clickProps); }} keyboardHandler={(e, clickProps, status) => { - // Handle right arrow key to advance tour if (e.key === 'ArrowRight' && !status?.isRightDisabled && clickProps) { e.preventDefault(); advanceTour(clickProps); - } - // Handle escape key to close tour - else if (e.key === 'Escape' && !status?.isEscDisabled && clickProps) { + } else if (e.key === 'Escape' && !status?.isEscDisabled && clickProps) { e.preventDefault(); handleCloseTour(clickProps); } @@ -487,22 +216,16 @@ export default function OnboardingTour() { }} components={{ Close: ({ onClick }) => ( - + ), - Content: ({ content } : {content: string}) => ( -
+ Content: ({ content }: { content: string }) => ( +
), }} > + ); } diff --git a/frontend/src/core/components/onboarding/ServerLicenseModal.tsx b/frontend/src/core/components/onboarding/ServerLicenseModal.tsx new file mode 100644 index 000000000..9c53628cb --- /dev/null +++ b/frontend/src/core/components/onboarding/ServerLicenseModal.tsx @@ -0,0 +1,118 @@ +import React from 'react'; +import { Modal, Button, Group, Stack } from '@mantine/core'; +import { useTranslation } from 'react-i18next'; +import AnimatedSlideBackground from '@app/components/onboarding/slides/AnimatedSlideBackground'; +import ServerLicenseSlide from '@app/components/onboarding/slides/ServerLicenseSlide'; +import { LicenseNotice } from '@app/types/types'; +import { Z_INDEX_OVER_FULLSCREEN_SURFACE } from '@app/styles/zIndex'; +import { BASE_PATH } from '@app/constants/app'; +import styles from '@app/components/onboarding/InitialOnboardingModal/InitialOnboardingModal.module.css'; + +interface ServerLicenseModalProps { + opened: boolean; + onClose: () => void; + onSeePlans?: () => void; + licenseNotice: LicenseNotice; +} + +export default function ServerLicenseModal({ + opened, + onClose, + onSeePlans, + licenseNotice, +}: ServerLicenseModalProps) { + const { t } = useTranslation(); + const slide = React.useMemo(() => ServerLicenseSlide({ licenseNotice }), [licenseNotice]); + const primaryLabel = licenseNotice.isOverLimit + ? t('onboarding.serverLicense.upgrade', 'Upgrade now →') + : t('onboarding.serverLicense.seePlans', 'See Plans →'); + const secondaryLabel = t('onboarding.serverLicense.skip', 'Skip for now'); + + const handleSeePlans = () => { + onSeePlans?.(); + onClose(); + }; + + const secondaryStyles = { + root: { + background: 'var(--onboarding-secondary-button-bg)', + border: '1px solid var(--onboarding-secondary-button-border)', + color: 'var(--onboarding-secondary-button-text)', + }, + }; + + const primaryStyles = { + root: { + background: 'var(--onboarding-primary-button-bg)', + color: 'var(--onboarding-primary-button-text)', + }, + }; + + return ( + + +
+ +
+
+ Stirling logo +
+
+
+ +
+ +
+ {slide.title} +
+
+ {slide.body} +
+ + + + +
+
+
+
+ ); +} + diff --git a/frontend/src/core/components/onboarding/TourContent.tsx b/frontend/src/core/components/onboarding/TourContent.tsx new file mode 100644 index 000000000..c0f787ec9 --- /dev/null +++ b/frontend/src/core/components/onboarding/TourContent.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { useTour } from '@reactour/tour'; +import { useOnboarding } from '@app/contexts/OnboardingContext'; + +export default function TourContent() { + const { isOpen } = useOnboarding(); + const { setIsOpen, setCurrentStep } = useTour(); + const previousIsOpenRef = React.useRef(isOpen); + + React.useEffect(() => { + const wasClosedNowOpen = !previousIsOpenRef.current && isOpen; + previousIsOpenRef.current = isOpen; + + if (wasClosedNowOpen) { + setCurrentStep(0); + } + setIsOpen(isOpen); + }, [isOpen, setIsOpen, setCurrentStep]); + + return null; +} + diff --git a/frontend/src/core/components/onboarding/adminStepsConfig.ts b/frontend/src/core/components/onboarding/adminStepsConfig.ts new file mode 100644 index 000000000..5eb993cc9 --- /dev/null +++ b/frontend/src/core/components/onboarding/adminStepsConfig.ts @@ -0,0 +1,133 @@ +import type { StepType } from '@reactour/tour'; +import type { TFunction } from 'i18next'; +import { AdminTourStep } from '@app/components/onboarding/tourSteps'; +import { addGlowToElements, removeAllGlows } from '@app/components/onboarding/tourGlow'; + +interface AdminStepActions { + saveAdminState: () => void; + openConfigModal: () => void; + navigateToSection: (section: string) => void; + scrollNavToSection: (section: string) => Promise | void; +} + +interface CreateAdminStepsConfigArgs { + t: TFunction; + actions: AdminStepActions; +} + +export function createAdminStepsConfig({ t, actions }: CreateAdminStepsConfigArgs): Record { + const { saveAdminState, openConfigModal, navigateToSection, scrollNavToSection } = actions; + + return { + [AdminTourStep.WELCOME]: { + selector: '[data-tour="config-button"]', + content: t('adminOnboarding.welcome', "Welcome to the Admin Tour! Let's explore the powerful enterprise features and settings available to system administrators."), + position: 'right', + padding: 10, + action: () => { + saveAdminState(); + }, + }, + [AdminTourStep.CONFIG_BUTTON]: { + selector: '[data-tour="config-button"]', + content: t('adminOnboarding.configButton', "Click the Config button to access all system settings and administrative controls."), + position: 'right', + padding: 10, + actionAfter: () => { + openConfigModal(); + }, + }, + [AdminTourStep.SETTINGS_OVERVIEW]: { + selector: '.modal-nav', + content: t('adminOnboarding.settingsOverview', "This is the Settings Panel. Admin settings are organised by category for easy navigation."), + position: 'right', + padding: 0, + action: () => { + removeAllGlows(); + }, + }, + [AdminTourStep.TEAMS_AND_USERS]: { + selector: '[data-tour="admin-people-nav"]', + highlightedSelectors: ['[data-tour="admin-people-nav"]', '[data-tour="admin-teams-nav"]', '[data-tour="settings-content-area"]'], + content: t('adminOnboarding.teamsAndUsers', "Manage Teams and individual users here. You can invite new users via email, shareable links, or create custom accounts for them yourself."), + position: 'right', + padding: 10, + action: () => { + removeAllGlows(); + navigateToSection('people'); + setTimeout(() => { + addGlowToElements(['[data-tour="admin-people-nav"]', '[data-tour="admin-teams-nav"]', '[data-tour="settings-content-area"]']); + }, 100); + }, + }, + [AdminTourStep.SYSTEM_CUSTOMIZATION]: { + selector: '[data-tour="admin-adminGeneral-nav"]', + highlightedSelectors: ['[data-tour="admin-adminGeneral-nav"]', '[data-tour="admin-adminFeatures-nav"]', '[data-tour="admin-adminEndpoints-nav"]', '[data-tour="settings-content-area"]'], + content: t('adminOnboarding.systemCustomization', "We have extensive ways to customise the UI: System Settings let you change the app name and languages, Features allows server certificate management, and Endpoints lets you enable or disable specific tools for your users."), + position: 'right', + padding: 10, + action: () => { + removeAllGlows(); + navigateToSection('adminGeneral'); + setTimeout(() => { + addGlowToElements(['[data-tour="admin-adminGeneral-nav"]', '[data-tour="admin-adminFeatures-nav"]', '[data-tour="admin-adminEndpoints-nav"]', '[data-tour="settings-content-area"]']); + }, 100); + }, + }, + [AdminTourStep.DATABASE_SECTION]: { + selector: '[data-tour="admin-adminDatabase-nav"]', + highlightedSelectors: ['[data-tour="admin-adminDatabase-nav"]', '[data-tour="settings-content-area"]'], + content: t('adminOnboarding.databaseSection', "For advanced production environments, we have settings to allow external database hookups so you can integrate with your existing infrastructure."), + position: 'right', + padding: 10, + action: () => { + removeAllGlows(); + navigateToSection('adminDatabase'); + setTimeout(() => { + addGlowToElements(['[data-tour="admin-adminDatabase-nav"]', '[data-tour="settings-content-area"]']); + }, 100); + }, + }, + [AdminTourStep.CONNECTIONS_SECTION]: { + selector: '[data-tour="admin-adminConnections-nav"]', + highlightedSelectors: ['[data-tour="admin-adminConnections-nav"]', '[data-tour="settings-content-area"]'], + content: t('adminOnboarding.connectionsSection', "The Connections section supports various login methods including custom SSO and SAML providers like Google and GitHub, plus email integrations for notifications and communications."), + position: 'right', + padding: 10, + action: () => { + removeAllGlows(); + navigateToSection('adminConnections'); + setTimeout(() => { + addGlowToElements(['[data-tour="admin-adminConnections-nav"]', '[data-tour="settings-content-area"]']); + }, 100); + }, + actionAfter: async () => { + await scrollNavToSection('adminAudit'); + }, + }, + [AdminTourStep.ADMIN_TOOLS]: { + selector: '[data-tour="admin-adminAudit-nav"]', + highlightedSelectors: ['[data-tour="admin-adminAudit-nav"]', '[data-tour="admin-adminUsage-nav"]', '[data-tour="settings-content-area"]'], + content: t('adminOnboarding.adminTools', "Finally, we have advanced administration tools like Auditing to track system activity and Usage Analytics to monitor how your users interact with the platform."), + position: 'right', + padding: 10, + action: () => { + removeAllGlows(); + navigateToSection('adminAudit'); + setTimeout(() => { + addGlowToElements(['[data-tour="admin-adminAudit-nav"]', '[data-tour="admin-adminUsage-nav"]', '[data-tour="settings-content-area"]']); + }, 100); + }, + }, + [AdminTourStep.WRAP_UP]: { + selector: '[data-tour="help-button"]', + content: t('adminOnboarding.wrapUp', "That's the admin tour! You've seen the enterprise features that make Stirling PDF a powerful, customisable solution for organisations. Access this tour anytime from the Help menu."), + position: 'right', + padding: 10, + action: () => { + removeAllGlows(); + }, + }, + }; +} + diff --git a/frontend/src/core/components/onboarding/hooks/useOnboardingFlow.ts b/frontend/src/core/components/onboarding/hooks/useOnboardingFlow.ts new file mode 100644 index 000000000..18dec59fc --- /dev/null +++ b/frontend/src/core/components/onboarding/hooks/useOnboardingFlow.ts @@ -0,0 +1,324 @@ +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { usePreferences } from '@app/contexts/PreferencesContext'; +import { useAppConfig } from '@app/contexts/AppConfigContext'; +import { useCookieConsentContext } from '@app/contexts/CookieConsentContext'; +import { useOnboarding } from '@app/contexts/OnboardingContext'; +import type { LicenseNotice } from '@app/types/types'; +import { useNavigate } from 'react-router-dom'; +import { + ONBOARDING_SESSION_BLOCK_KEY, + ONBOARDING_SESSION_EVENT, + SERVER_LICENSE_REQUEST_EVENT, + type ServerLicenseRequestPayload, +} from '@app/constants/events'; +import { useServerExperience } from '@app/hooks/useServerExperience'; + +interface InitialModalHandlers { + opened: boolean; + onLicenseNoticeUpdate: (notice: LicenseNotice) => void; + onRequestServerLicense: (options?: { deferUntilTourComplete?: boolean; selfReportedAdmin?: boolean }) => void; + onClose: () => void; +} + +interface ServerLicenseModalHandlers { + opened: boolean; + licenseNotice: LicenseNotice; + onClose: () => void; + onSeePlans: () => void; +} + +export function useOnboardingFlow() { + const { preferences, updatePreference } = usePreferences(); + const { config } = useAppConfig(); + const { showCookieConsent, isReady: isCookieConsentReady } = useCookieConsentContext(); + const { completeTour, tourType, isOpen } = useOnboarding(); + + const shouldShowIntro = !preferences.hasSeenIntroOnboarding; + const isAdminUser = !!config?.isAdmin; + const { hasPaidLicense } = useServerExperience(); + + const [licenseNotice, setLicenseNotice] = useState({ + totalUsers: null, + freeTierLimit: 5, + isOverLimit: false, + requiresLicense: false, + }); + const [cookieBannerPending, setCookieBannerPending] = useState(false); + const [serverLicenseIntent, setServerLicenseIntent] = useState<'idle' | 'pending' | 'deferred'>('idle'); + const [serverLicenseSource, setServerLicenseSource] = useState<'config' | 'self-reported' | null>(null); + const [isServerLicenseOpen, setIsServerLicenseOpen] = useState(false); + const [hasShownServerLicense, setHasShownServerLicense] = useState(false); + const [toolPromptCompleted, setToolPromptCompleted] = useState( + preferences.toolPanelModePromptSeen || preferences.hasSelectedToolPanelMode, + ); + const introWasOpenRef = useRef(false); + const navigate = useNavigate(); + const onboardingSessionMarkedRef = useRef(false); + + const handleInitialModalClose = useCallback(() => { + if (!preferences.hasSeenIntroOnboarding) { + updatePreference('hasSeenIntroOnboarding', true); + } + }, [preferences.hasSeenIntroOnboarding, updatePreference]); + + const handleLicenseNoticeUpdate = useCallback((notice: LicenseNotice) => { + setLicenseNotice(notice); + }, []); + + const handleToolPromptComplete = useCallback(() => { + setToolPromptCompleted(true); + }, []); + + const maybeShowCookieBanner = useCallback(() => { + if (preferences.hasSeenCookieBanner) { + return; + } + + if (!isCookieConsentReady || isServerLicenseOpen || serverLicenseIntent !== 'idle' || !toolPromptCompleted) { + setCookieBannerPending(true); + return; + } + + setCookieBannerPending(false); + showCookieConsent(); + updatePreference('hasSeenCookieBanner', true); + }, [ + isCookieConsentReady, + isServerLicenseOpen, + preferences.hasSeenCookieBanner, + serverLicenseIntent, + showCookieConsent, + toolPromptCompleted, + updatePreference, + ]); + + const requestServerLicense = useCallback( + ({ + deferUntilTourComplete = false, + selfReportedAdmin = false, + }: { deferUntilTourComplete?: boolean; selfReportedAdmin?: boolean } = {}) => { + const qualifies = isAdminUser || selfReportedAdmin; + if (!qualifies) { + return; + } + if (hasPaidLicense || !licenseNotice.requiresLicense) { + return; + } + setServerLicenseSource(isAdminUser ? 'config' : 'self-reported'); + setServerLicenseIntent((prev) => { + if (prev === 'pending') { + return prev; + } + if (prev === 'deferred') { + return deferUntilTourComplete ? prev : 'pending'; + } + if (prev === 'idle') { + return deferUntilTourComplete ? 'deferred' : 'pending'; + } + return prev; + }); + }, + [hasPaidLicense, isAdminUser, licenseNotice.requiresLicense], + ); + + useEffect(() => { + if (typeof window === 'undefined') { + return; + } + + const handleServerLicenseRequested = (event: Event) => { + const { detail } = event as CustomEvent; + + if (detail?.licenseNotice) { + setLicenseNotice((prev) => ({ + ...prev, + ...detail.licenseNotice, + totalUsers: + detail.licenseNotice?.totalUsers ?? prev.totalUsers, + freeTierLimit: + detail.licenseNotice?.freeTierLimit ?? prev.freeTierLimit, + isOverLimit: + detail.licenseNotice?.isOverLimit ?? prev.isOverLimit, + requiresLicense: + detail.licenseNotice?.requiresLicense ?? prev.requiresLicense, + })); + } + + requestServerLicense({ + deferUntilTourComplete: detail?.deferUntilTourComplete ?? false, + selfReportedAdmin: detail?.selfReportedAdmin ?? false, + }); + }; + + window.addEventListener( + SERVER_LICENSE_REQUEST_EVENT, + handleServerLicenseRequested as EventListener, + ); + + return () => { + window.removeEventListener( + SERVER_LICENSE_REQUEST_EVENT, + handleServerLicenseRequested as EventListener, + ); + }; + }, [requestServerLicense]); + + useEffect(() => { + if ( + cookieBannerPending && + isCookieConsentReady && + serverLicenseIntent === 'idle' && + !isServerLicenseOpen && + toolPromptCompleted + ) { + maybeShowCookieBanner(); + } + }, [ + cookieBannerPending, + isCookieConsentReady, + isServerLicenseOpen, + serverLicenseIntent, + toolPromptCompleted, + maybeShowCookieBanner, + ]); + + useEffect(() => { + const isEligibleAdmin = + isAdminUser || serverLicenseSource === 'self-reported' || licenseNotice.requiresLicense; + if ( + introWasOpenRef.current && + !shouldShowIntro && + isEligibleAdmin && + toolPromptCompleted && + !hasShownServerLicense && + licenseNotice.requiresLicense && + serverLicenseIntent === 'idle' + ) { + if (!serverLicenseSource) { + setServerLicenseSource(isAdminUser ? 'config' : 'self-reported'); + } + setServerLicenseIntent('pending'); + } + introWasOpenRef.current = shouldShowIntro; + }, [ + hasShownServerLicense, + isAdminUser, + serverLicenseIntent, + shouldShowIntro, + serverLicenseSource, + toolPromptCompleted, + licenseNotice.requiresLicense, + ]); + + useEffect(() => { + const isEligibleAdmin = + isAdminUser || serverLicenseSource === 'self-reported' || licenseNotice.requiresLicense; + if ( + serverLicenseIntent !== 'idle' && + !shouldShowIntro && + !isOpen && + !isServerLicenseOpen && + isEligibleAdmin && + toolPromptCompleted && + licenseNotice.requiresLicense + ) { + setIsServerLicenseOpen(true); + setServerLicenseIntent(serverLicenseIntent === 'deferred' ? 'pending' : 'idle'); + } + }, [ + isAdminUser, + isOpen, + isServerLicenseOpen, + serverLicenseIntent, + shouldShowIntro, + serverLicenseSource, + toolPromptCompleted, + licenseNotice.requiresLicense, + ]); + + const handleServerLicenseClose = useCallback(() => { + setIsServerLicenseOpen(false); + setHasShownServerLicense(true); + setServerLicenseIntent('idle'); + setServerLicenseSource(null); + maybeShowCookieBanner(); + }, [maybeShowCookieBanner]); + + useEffect(() => { + if (onboardingSessionMarkedRef.current) { + return; + } + if (typeof window === 'undefined') { + return; + } + if (shouldShowIntro || isOpen) { + onboardingSessionMarkedRef.current = true; + window.sessionStorage.setItem(ONBOARDING_SESSION_BLOCK_KEY, 'true'); + window.dispatchEvent(new CustomEvent(ONBOARDING_SESSION_EVENT)); + } + }, [isOpen, shouldShowIntro]); + + useEffect(() => { + if (typeof window === 'undefined') { + return; + } + if (!shouldShowIntro && !isOpen) { + window.sessionStorage.removeItem(ONBOARDING_SESSION_BLOCK_KEY); + window.dispatchEvent(new CustomEvent(ONBOARDING_SESSION_EVENT)); + } + }, [isOpen, shouldShowIntro]); + + const handleServerLicenseSeePlans = useCallback(() => { + handleServerLicenseClose(); + navigate('/settings/adminPlan'); + }, [handleServerLicenseClose, navigate]); + + const handleTourCompletion = useCallback(() => { + completeTour(); + if (serverLicenseIntent === 'deferred') { + setServerLicenseIntent('pending'); + } else if (tourType === 'admin' && (isAdminUser || serverLicenseSource === 'self-reported')) { + setServerLicenseSource((prev) => prev ?? (isAdminUser ? 'config' : 'self-reported')); + setServerLicenseIntent((prev) => (prev === 'pending' ? prev : 'pending')); + } + maybeShowCookieBanner(); + }, [ + completeTour, + isAdminUser, + maybeShowCookieBanner, + serverLicenseIntent, + serverLicenseSource, + tourType, + ]); + + const initialModalProps: InitialModalHandlers = useMemo( + () => ({ + opened: shouldShowIntro, + onLicenseNoticeUpdate: handleLicenseNoticeUpdate, + onRequestServerLicense: requestServerLicense, + onClose: handleInitialModalClose, + }), + [handleInitialModalClose, handleLicenseNoticeUpdate, requestServerLicense, shouldShowIntro], + ); + + const serverLicenseModalProps: ServerLicenseModalHandlers = useMemo( + () => ({ + opened: isServerLicenseOpen, + licenseNotice, + onClose: handleServerLicenseClose, + onSeePlans: handleServerLicenseSeePlans, + }), + [handleServerLicenseClose, handleServerLicenseSeePlans, isServerLicenseOpen, licenseNotice], + ); + + return { + tourType, + isTourOpen: isOpen, + maskClassName: tourType === 'admin' ? 'admin-tour-mask' : undefined, + initialModalProps, + handleToolPromptComplete, + serverLicenseModalProps, + handleTourCompletion, + }; +} + diff --git a/frontend/src/core/components/onboarding/onboardingFlowConfig.ts b/frontend/src/core/components/onboarding/onboardingFlowConfig.ts new file mode 100644 index 000000000..f9c08eb60 --- /dev/null +++ b/frontend/src/core/components/onboarding/onboardingFlowConfig.ts @@ -0,0 +1,207 @@ +import WelcomeSlide from '@app/components/onboarding/slides/WelcomeSlide'; +import DesktopInstallSlide from '@app/components/onboarding/slides/DesktopInstallSlide'; +import SecurityCheckSlide from '@app/components/onboarding/slides/SecurityCheckSlide'; +import PlanOverviewSlide from '@app/components/onboarding/slides/PlanOverviewSlide'; +import ServerLicenseSlide from '@app/components/onboarding/slides/ServerLicenseSlide'; +import { SlideConfig, LicenseNotice } from '@app/types/types'; + +export type SlideId = + | 'welcome' + | 'desktop-install' + | 'security-check' + | 'admin-overview' + | 'server-license'; + +export type HeroType = 'rocket' | 'dual-icon' | 'shield' | 'diamond' | 'logo'; + +export type ButtonAction = + | 'next' + | 'prev' + | 'close' + | 'complete-close' + | 'download-selected' + | 'security-next' + | 'launch-admin' + | 'launch-tools' + | 'launch-auto' + | 'see-plans' + | 'skip-to-license'; + +export interface FlowState { + selectedRole: 'admin' | 'user' | null; +} + +export interface OSOption { + label: string; + url: string; + value: string; +} + +export interface SlideFactoryParams { + osLabel: string; + osUrl: string; + osOptions?: OSOption[]; + onDownloadUrlChange?: (url: string) => void; + selectedRole: 'admin' | 'user' | null; + onRoleSelect: (role: 'admin' | 'user' | null) => void; + licenseNotice?: LicenseNotice; + loginEnabled?: boolean; +} + +export interface HeroDefinition { + type: HeroType; +} + +export interface ButtonDefinition { + key: string; + type: 'button' | 'icon'; + label?: string; + icon?: 'chevron-left'; + variant?: 'primary' | 'secondary' | 'default'; + group: 'left' | 'right'; + action: ButtonAction; + disabledWhen?: (state: FlowState) => boolean; +} + +export interface SlideDefinition { + id: SlideId; + createSlide: (params: SlideFactoryParams) => SlideConfig; + hero: HeroDefinition; + buttons: ButtonDefinition[]; +} + +export const SLIDE_DEFINITIONS: Record = { + 'welcome': { + id: 'welcome', + createSlide: () => WelcomeSlide(), + hero: { type: 'rocket' }, + buttons: [ + { + key: 'welcome-next', + type: 'button', + label: 'onboarding.buttons.next', + variant: 'primary', + group: 'right', + action: 'next', + }, + ], + }, + 'desktop-install': { + id: 'desktop-install', + createSlide: ({ osLabel, osUrl, osOptions, onDownloadUrlChange }) => DesktopInstallSlide({ osLabel, osUrl, osOptions, onDownloadUrlChange }), + hero: { type: 'dual-icon' }, + buttons: [ + { + key: 'desktop-back', + type: 'icon', + icon: 'chevron-left', + group: 'left', + action: 'prev', + }, + { + key: 'desktop-skip', + type: 'button', + label: 'onboarding.buttons.skipForNow', + variant: 'secondary', + group: 'left', + action: 'next', + }, + { + key: 'desktop-download', + type: 'button', + label: 'onboarding.buttons.download', + variant: 'primary', + group: 'right', + action: 'download-selected', + }, + ], + }, + 'security-check': { + id: 'security-check', + createSlide: ({ selectedRole, onRoleSelect }) => + SecurityCheckSlide({ selectedRole, onRoleSelect }), + hero: { type: 'shield' }, + buttons: [ + { + key: 'security-back', + type: 'button', + label: 'onboarding.buttons.back', + variant: 'secondary', + group: 'left', + action: 'prev', + }, + { + key: 'security-next', + type: 'button', + label: 'onboarding.buttons.next', + variant: 'primary', + group: 'right', + action: 'security-next', + disabledWhen: (state) => !state.selectedRole, + }, + ], + }, + 'admin-overview': { + id: 'admin-overview', + createSlide: ({ licenseNotice, loginEnabled }) => + PlanOverviewSlide({ isAdmin: true, licenseNotice, loginEnabled }), + hero: { type: 'diamond' }, + buttons: [ + { + key: 'admin-back', + type: 'icon', + icon: 'chevron-left', + group: 'left', + action: 'prev', + }, + { + key: 'admin-show', + type: 'button', + label: 'onboarding.buttons.showMeAround', + variant: 'primary', + group: 'right', + action: 'launch-admin', + }, + { + key: 'admin-skip', + type: 'button', + label: 'onboarding.buttons.skipTheTour', + variant: 'secondary', + group: 'left', + action: 'skip-to-license', + }, + ], + }, + 'server-license': { + id: 'server-license', + createSlide: ({ licenseNotice }) => ServerLicenseSlide({ licenseNotice }), + hero: { type: 'logo' }, + buttons: [ + { + key: 'license-close', + type: 'button', + label: 'onboarding.buttons.skipForNow', + variant: 'secondary', + group: 'left', + action: 'close', + }, + { + key: 'license-see-plans', + type: 'button', + label: 'onboarding.serverLicense.seePlans', + variant: 'primary', + group: 'right', + action: 'see-plans', + }, + ], + }, +}; + +export const FLOW_SEQUENCES = { + loginAdmin: ['welcome', 'desktop-install', 'admin-overview'] as SlideId[], + loginUser: ['welcome', 'desktop-install'] as SlideId[], + noLoginBase: ['welcome', 'desktop-install', 'security-check'] as SlideId[], + noLoginAdmin: ['admin-overview'] as SlideId[], +}; + + diff --git a/frontend/src/core/components/onboarding/slides/AnimatedSlideBackground.module.css b/frontend/src/core/components/onboarding/slides/AnimatedSlideBackground.module.css new file mode 100644 index 000000000..5e5799ec2 --- /dev/null +++ b/frontend/src/core/components/onboarding/slides/AnimatedSlideBackground.module.css @@ -0,0 +1,70 @@ +.hero { + position: relative; + width: 100%; + height: 220px; + overflow: hidden; + border-radius: 0; +} + +.gradientLayer { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-size: 180% 180%; + opacity: 0; + transition: opacity 0.8s ease-in-out; + z-index: 1; +} + +.gradientLayerActive { + opacity: 1; + animation: gradientShift 18s ease-in-out infinite alternate; + z-index: 2; +} + +.gradientLayerPrev { + opacity: 1; + z-index: 3; + transition: opacity 0.8s ease-in-out; +} + +.gradientLayerPrevFadeOut { + opacity: 0; + z-index: 3; + transition: opacity 0.8s ease-in-out; +} + +.circle { + position: absolute; + border-radius: 50%; + pointer-events: none; + box-shadow: 0 18px 36px rgba(15, 23, 42, 0.12); + animation-name: circleSway; + animation-timing-function: ease-in-out; + animation-iteration-count: infinite; + animation-direction: alternate; + animation-duration: var(--circle-duration, 15s); + animation-delay: var(--circle-delay, 0s); + will-change: transform; + z-index: 10; +} + +@keyframes gradientShift { + 0% { + background-position: 0% 50%; + } + 100% { + background-position: 100% 50%; + } +} + +@keyframes circleSway { + 0% { + transform: translate3d(0, 0, 0); + } + 100% { + transform: translate3d(var(--circle-move-x, 40px), var(--circle-move-y, 24px), 0); + } +} diff --git a/frontend/src/core/components/onboarding/slides/AnimatedSlideBackground.tsx b/frontend/src/core/components/onboarding/slides/AnimatedSlideBackground.tsx new file mode 100644 index 000000000..289b07b6c --- /dev/null +++ b/frontend/src/core/components/onboarding/slides/AnimatedSlideBackground.tsx @@ -0,0 +1,115 @@ +import React from 'react'; +import styles from '@app/components/onboarding/slides/AnimatedSlideBackground.module.css'; +import { AnimatedSlideBackgroundProps } from '@app/types/types'; + +type CircleStyles = React.CSSProperties & { + '--circle-move-x'?: string; + '--circle-move-y'?: string; + '--circle-duration'?: string; + '--circle-delay'?: string; +}; + +interface AnimatedSlideBackgroundComponentProps extends AnimatedSlideBackgroundProps { + isActive: boolean; + slideKey: string; +} + +export default function AnimatedSlideBackground({ + gradientStops, + circles, + isActive, +}: AnimatedSlideBackgroundComponentProps) { + const [prevGradient, setPrevGradient] = React.useState<[string, string] | null>(null); + const [currentGradient, setCurrentGradient] = React.useState<[string, string]>(gradientStops); + const [isTransitioning, setIsTransitioning] = React.useState(false); + const isFirstMount = React.useRef(true); + + React.useEffect(() => { + // Skip transition on first mount + if (isFirstMount.current) { + isFirstMount.current = false; + setCurrentGradient(gradientStops); + return; + } + + // Only transition if gradient actually changed + if (currentGradient[0] !== gradientStops[0] || currentGradient[1] !== gradientStops[1]) { + // Store previous gradient and start transition + setPrevGradient(currentGradient); + setIsTransitioning(true); + + // Update to new gradient (will fade in) + setCurrentGradient(gradientStops); + } + }, [gradientStops]); + + const currentGradientStyle = React.useMemo( + () => ({ + backgroundImage: `linear-gradient(135deg, ${currentGradient[0]}, ${currentGradient[1]})`, + }), + [currentGradient], + ); + + const prevGradientStyle = prevGradient + ? { + backgroundImage: `linear-gradient(135deg, ${prevGradient[0]}, ${prevGradient[1]})`, + } + : null; + + return ( +
+ {prevGradientStyle && isTransitioning && ( +
{ + setPrevGradient(null); + setIsTransitioning(false); + }} + /> + )} +
+ {circles.map((circle, index) => { + const { position, size, color, opacity, blur, amplitude = 48, duration = 15, delay = 0 } = circle; + + const moveX = position === 'bottom-left' ? amplitude : -amplitude; + const moveY = position === 'bottom-left' ? -amplitude * 0.6 : amplitude * 0.6; + + const circleStyle: CircleStyles = { + width: size, + height: size, + background: color, + opacity: opacity ?? 0.9, + filter: blur ? `blur(${blur}px)` : undefined, + '--circle-move-x': `${moveX}px`, + '--circle-move-y': `${moveY}px`, + '--circle-duration': `${duration}s`, + '--circle-delay': `${delay}s`, + }; + + const defaultOffset = -size / 2; + const offsetX = circle.offsetX ?? 0; + const offsetY = circle.offsetY ?? 0; + + if (position === 'bottom-left') { + circleStyle.left = `${defaultOffset + offsetX}px`; + circleStyle.bottom = `${defaultOffset + offsetY}px`; + } else { + circleStyle.right = `${defaultOffset + offsetX}px`; + circleStyle.top = `${defaultOffset + offsetY}px`; + } + + return ( +
+ ); + })} +
+ ); +} diff --git a/frontend/src/core/components/onboarding/slides/DesktopInstallSlide.tsx b/frontend/src/core/components/onboarding/slides/DesktopInstallSlide.tsx new file mode 100644 index 000000000..9cebfbb9c --- /dev/null +++ b/frontend/src/core/components/onboarding/slides/DesktopInstallSlide.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { SlideConfig } from '@app/types/types'; +import { UNIFIED_CIRCLE_CONFIG } from '@app/components/onboarding/slides/unifiedBackgroundConfig'; +import { DesktopInstallTitle, type OSOption } from '@app/components/onboarding/slides/DesktopInstallTitle'; + +export type { OSOption }; + +interface DesktopInstallSlideProps { + osLabel: string; + osUrl: string; + osOptions?: OSOption[]; + onDownloadUrlChange?: (url: string) => void; +} + +const DesktopInstallBody = () => { + const { t } = useTranslation(); + + return ( + + {t( + 'onboarding.desktopInstall.body', + 'Stirling works best as a desktop app. You can use it offline, access documents faster, and make edits locally on your computer.', + )} + + ); +}; + +export default function DesktopInstallSlide({ + osLabel, + osUrl, + osOptions = [], + onDownloadUrlChange, +}: DesktopInstallSlideProps): SlideConfig { + + return { + key: 'desktop-install', + title: ( + + ), + body: , + downloadUrl: osUrl, + background: { + gradientStops: ['#2563EB', '#0EA5E9'], + circles: UNIFIED_CIRCLE_CONFIG, + }, + }; +} + diff --git a/frontend/src/core/components/onboarding/slides/DesktopInstallTitle.tsx b/frontend/src/core/components/onboarding/slides/DesktopInstallTitle.tsx new file mode 100644 index 000000000..ac42b518b --- /dev/null +++ b/frontend/src/core/components/onboarding/slides/DesktopInstallTitle.tsx @@ -0,0 +1,93 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Menu, ActionIcon } from '@mantine/core'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; + +export interface OSOption { + label: string; + url: string; + value: string; +} + +interface DesktopInstallTitleProps { + osLabel: string; + osUrl: string; + osOptions: OSOption[]; + onDownloadUrlChange?: (url: string) => void; +} + +export const DesktopInstallTitle: React.FC = ({ + osLabel, + osUrl, + osOptions, + onDownloadUrlChange +}) => { + const { t } = useTranslation(); + const [selectedOsUrl, setSelectedOsUrl] = React.useState(osUrl); + + React.useEffect(() => { + setSelectedOsUrl(osUrl); + }, [osUrl]); + + const handleOsSelect = React.useCallback((option: OSOption) => { + setSelectedOsUrl(option.url); + onDownloadUrlChange?.(option.url); + }, [onDownloadUrlChange]); + + const currentOsOption = osOptions.find(opt => opt.url === selectedOsUrl) || + (osOptions.length > 0 ? osOptions[0] : { label: osLabel, url: osUrl }); + + const displayLabel = currentOsOption.label || osLabel; + const title = displayLabel + ? t('onboarding.desktopInstall.titleWithOs', 'Download for {{osLabel}}', { osLabel: displayLabel }) + : t('onboarding.desktopInstall.title', 'Download'); + + // If only one option or no options, don't show dropdown + if (osOptions.length <= 1) { + return
{title}
; + } + + return ( +
+ {title} + + + + + + + + {osOptions.map((option) => { + const isSelected = option.url === selectedOsUrl; + return ( + handleOsSelect(option)} + style={{ + backgroundColor: isSelected + ? 'light-dark(var(--mantine-color-blue-1), var(--mantine-color-blue-8))' + : 'transparent', + color: isSelected + ? 'light-dark(var(--mantine-color-blue-9), var(--mantine-color-white))' + : 'inherit', + }} + > + {option.label} + + ); + })} + + +
+ ); +}; + diff --git a/frontend/src/core/components/onboarding/slides/PlanOverviewSlide.tsx b/frontend/src/core/components/onboarding/slides/PlanOverviewSlide.tsx new file mode 100644 index 000000000..3b8d4bfb0 --- /dev/null +++ b/frontend/src/core/components/onboarding/slides/PlanOverviewSlide.tsx @@ -0,0 +1,83 @@ +import React from 'react'; +import { Trans, useTranslation } from 'react-i18next'; +import { SlideConfig, LicenseNotice } from '@app/types/types'; +import { UNIFIED_CIRCLE_CONFIG } from '@app/components/onboarding/slides/unifiedBackgroundConfig'; + +interface PlanOverviewSlideProps { + isAdmin: boolean; + licenseNotice?: LicenseNotice; + loginEnabled?: boolean; +} + +const DEFAULT_FREE_TIER_LIMIT = 5; + +const PlanOverviewTitle: React.FC<{ isAdmin: boolean }> = ({ isAdmin }) => { + const { t } = useTranslation(); + return ( + <> + {isAdmin + ? t('onboarding.planOverview.adminTitle', 'Admin Overview') + : t('onboarding.planOverview.userTitle', 'Plan Overview')} + + ); +}; + +const AdminOverviewBody: React.FC<{ freeTierLimit: number; loginEnabled: boolean }> = ({ + freeTierLimit, + loginEnabled, +}) => { + const adminBodyKey = loginEnabled + ? 'onboarding.planOverview.adminBodyLoginEnabled' + : 'onboarding.planOverview.adminBodyLoginDisabled'; + + const defaultValue = loginEnabled + ? 'As an admin, you can manage users, configure settings, and monitor server health. The first {{freeTierLimit}} people on your server get to use Stirling free of charge.' + : 'Once you enable login mode, you can manage users, configure settings, and monitor server health. The first {{freeTierLimit}} people on your server get to use Stirling free of charge.'; + + return ( + }} + defaults={defaultValue} + /> + ); +}; + +const UserOverviewBody: React.FC = () => { + const { t } = useTranslation(); + return ( + + {t( + 'onboarding.planOverview.userBody', + "Invite teammates, assign roles, and keep your documents organized in one secure workspace. Enable login mode whenever you're ready to grow beyond solo use.", + )} + + ); +}; + +const PlanOverviewBody: React.FC<{ isAdmin: boolean; freeTierLimit: number; loginEnabled: boolean }> = ({ + isAdmin, + freeTierLimit, + loginEnabled, +}) => + isAdmin ? : ; + +export default function PlanOverviewSlide({ + isAdmin, + licenseNotice, + loginEnabled = false, +}: PlanOverviewSlideProps): SlideConfig { + const freeTierLimit = licenseNotice?.freeTierLimit ?? DEFAULT_FREE_TIER_LIMIT; + + return { + key: isAdmin ? 'admin-overview' : 'plan-overview', + title: , + body: , + background: { + gradientStops: isAdmin ? ['#4F46E5', '#0EA5E9'] : ['#F97316', '#EF4444'], + circles: UNIFIED_CIRCLE_CONFIG, + }, + }; +} + diff --git a/frontend/src/core/components/onboarding/slides/SecurityCheckSlide.tsx b/frontend/src/core/components/onboarding/slides/SecurityCheckSlide.tsx new file mode 100644 index 000000000..0efb2f591 --- /dev/null +++ b/frontend/src/core/components/onboarding/slides/SecurityCheckSlide.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { Select } from '@mantine/core'; +import { SlideConfig } from '@app/types/types'; +import LocalIcon from '@app/components/shared/LocalIcon'; +import { UNIFIED_CIRCLE_CONFIG } from '@app/components/onboarding/slides/unifiedBackgroundConfig'; +import i18n from '@app/i18n'; +import styles from '@app/components/onboarding/InitialOnboardingModal/InitialOnboardingModal.module.css'; + +interface SecurityCheckSlideProps { + selectedRole: 'admin' | 'user' | null; + onRoleSelect: (role: 'admin' | 'user' | null) => void; +} + +export default function SecurityCheckSlide({ + selectedRole, + onRoleSelect, +}: SecurityCheckSlideProps): SlideConfig { + return { + key: 'security-check', + title: 'Security Check', + body: ( +
+
+
+ + {i18n.t('onboarding.securityCheck.message', 'The application has undergone significant changes recently. Your server admin\'s attention may be required. Please confirm your role to continue.')} +
+ +