diff --git a/frontend/.env.sample b/frontend/.env.sample index d994df4..e1e9b82 100644 --- a/frontend/.env.sample +++ b/frontend/.env.sample @@ -1,4 +1,5 @@ VITE_API_URL="http://localhost:8002" VITE_API_PREFIX="/api/v1" -GROQ_API_KEY="your_groq_key_here" VITE_AWS_PUBLIC_URL="https://assets.threadzip.com" +VITE_ANALYTICS_ENABLED="false" +VITE_GA_MEASUREMENT_ID="" diff --git a/frontend/README.md b/frontend/README.md index e900a6a..216519b 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,12 +1,36 @@ -# TZ-Fabric +# TZ-Fabric frontend -![Node Version](https://badgen.net/badge/Node/20+/blue) +## Requirements + +- Node.js `^20.19.0` or `>=22.12.0` +- npm 10 or newer + +## Development ```sh -npm install +npm ci npm run dev ``` -Note: create `.env` file and put content from `.env.sample` file +Copy `.env.sample` to `.env` and configure the documented `VITE_` variables. +The development proxy target can be changed with `VITE_API_PROXY_TARGET`. + +## Quality checks + +```sh +npm run lint +npm test +npm run build +``` + +## Production preview process + +Build first, then serve the generated `dist` directory on port 5173: + +```sh +npm run build +npm start +``` -NOTE: the `VITE_API_URL` and it should be same as _vite.config.ts_ +`npm start` is intended for the repository's existing PM2 deployment workflow. +A dedicated static web server or CDN is preferable for a larger production setup. diff --git a/frontend/biome.json b/frontend/biome.json index 893a4ff..eee8c0f 100644 --- a/frontend/biome.json +++ b/frontend/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.3.10/schema.json", + "$schema": "https://biomejs.dev/schemas/2.5.4/schema.json", "vcs": { "enabled": false, "clientKind": "git", @@ -24,8 +24,21 @@ "linter": { "enabled": true, "rules": { - "recommended": true, - "a11y": "off", + "preset": "recommended", + "a11y": { + "recommended": true, + "useButtonType": "off", + "noSvgWithoutTitle": "off", + "useSemanticElements": "off", + "useKeyWithClickEvents": "off", + "noStaticElementInteractions": "off", + "useMediaCaption": "off", + "useFocusableInteractive": "off", + "noLabelWithoutControl": "off", + "noRedundantAlt": "off", + "noNoninteractiveElementToInteractiveRole": "off", + "noInteractiveElementToNoninteractiveRole": "off" + }, "suspicious": { "noArrayIndexKey": "off", "noExplicitAny": "warn" @@ -52,5 +65,22 @@ "organizeImports": "on" } } - } -} \ No newline at end of file + }, + "overrides": [ + { + "includes": [ + "src/**/*.css" + ], + "linter": { + "rules": { + "style": { + "noDescendingSpecificity": "off" + }, + "suspicious": { + "noDuplicateProperties": "off" + } + } + } + } + ] +} diff --git a/frontend/index.html b/frontend/index.html index 29be038..e68001e 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -5,6 +5,7 @@ + Fabric AI @@ -69,11 +70,158 @@ + +
- + + + + + - \ No newline at end of file + diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 26f34a6..5f5e5fa 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,7 +9,7 @@ "version": "1.5.1", "license": "MIT", "dependencies": { - "axios": "^1.11.0", + "axios": "^1.18.1", "dotenv-expand": "^12.0.3", "html-to-image": "^1.11.13", "jspdf": "^4.2.1", @@ -19,11 +19,11 @@ "react-ga4": "^3.0.1", "react-icons": "^5.5.0", "react-markdown": "^10.1.0", - "react-router-dom": "^7.7.1", + "react-router-dom": "^7.18.1", "remark-gfm": "^4.0.1" }, "devDependencies": { - "@biomejs/biome": "^2.0.2", + "@biomejs/biome": "2.5.4", "@eslint/js": "^9.30.1", "@types/node": "^24.10.1", "@types/react": "^19.1.9", @@ -35,7 +35,12 @@ "globals": "^16.3.0", "typescript": "~5.8.3", "typescript-eslint": "^8.35.1", - "vite": "^7.0.4" + "vite": "^7.3.6", + "vitest": "^4.1.10" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0", + "npm": ">=10" }, "funding": { "type": "individual", @@ -43,11 +48,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.27.1", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -56,7 +63,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.5", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", "engines": { @@ -64,19 +73,21 @@ } }, "node_modules/@babel/core": { - "version": "7.28.5", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -93,12 +104,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.5", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -108,12 +121,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -123,7 +138,9 @@ } }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, "license": "MIT", "engines": { @@ -131,25 +148,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -159,7 +180,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "dev": true, "license": "MIT", "engines": { @@ -167,7 +190,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, "license": "MIT", "engines": { @@ -175,7 +200,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -183,7 +210,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "engines": { @@ -191,23 +220,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.4", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.5", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.5" + "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -217,11 +250,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -231,11 +266,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -245,38 +282,42 @@ } }, "node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.27.2", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.5", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", "debug": "^4.3.1" }, "engines": { @@ -284,19 +325,23 @@ } }, "node_modules/@babel/types": { - "version": "7.28.5", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@biomejs/biome": { - "version": "2.3.10", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.4.tgz", + "integrity": "sha512-xy5FNE5kQJKyK5MR1gJy6ztXYx4WBAbYGlK04lMEgmyPRWKybY9NFwiG9yo0XdzOU8Xvhj41u034J1ywfoWfMw==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -310,20 +355,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.3.10", - "@biomejs/cli-darwin-x64": "2.3.10", - "@biomejs/cli-linux-arm64": "2.3.10", - "@biomejs/cli-linux-arm64-musl": "2.3.10", - "@biomejs/cli-linux-x64": "2.3.10", - "@biomejs/cli-linux-x64-musl": "2.3.10", - "@biomejs/cli-win32-arm64": "2.3.10", - "@biomejs/cli-win32-x64": "2.3.10" + "@biomejs/cli-darwin-arm64": "2.5.4", + "@biomejs/cli-darwin-x64": "2.5.4", + "@biomejs/cli-linux-arm64": "2.5.4", + "@biomejs/cli-linux-arm64-musl": "2.5.4", + "@biomejs/cli-linux-x64": "2.5.4", + "@biomejs/cli-linux-x64-musl": "2.5.4", + "@biomejs/cli-win32-arm64": "2.5.4", + "@biomejs/cli-win32-x64": "2.5.4" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.3.10.tgz", - "integrity": "sha512-M6xUjtCVnNGFfK7HMNKa593nb7fwNm43fq1Mt71kpLpb+4mE7odO8W/oWVDyBVO4ackhresy1ZYO7OJcVo/B7w==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.4.tgz", + "integrity": "sha512-4o3NFRobXHynkgcFVrlZsoDAFtF2ldlEGN8sORSws5ZQqyY4PXnPUIylu4ksfyHuwkfvDREuWh3JK+niRwGq3w==", "cpu": [ "arm64" ], @@ -338,9 +383,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.3.10.tgz", - "integrity": "sha512-Vae7+V6t/Avr8tVbFNjnFSTKZogZHFYl7MMH62P/J1kZtr0tyRQ9Fe0onjqjS2Ek9lmNLmZc/VR5uSekh+p1fg==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.4.tgz", + "integrity": "sha512-D32P5HkU2Y6PySuC/WsVDTOgsDwVFmujzhhhOQjajtATpVWFDXuVd3oRbsWNSEA+aaFzyzZm22szsyydBYlSyQ==", "cpu": [ "x64" ], @@ -355,13 +400,16 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.3.10.tgz", - "integrity": "sha512-hhPw2V3/EpHKsileVOFynuWiKRgFEV48cLe0eA+G2wO4SzlwEhLEB9LhlSrVeu2mtSn205W283LkX7Fh48CaxA==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.4.tgz", + "integrity": "sha512-pSEfW7B8kTsXUjUxC1xVVK+y85Ht3C5XxZ9gclmC7/3Ku9Vqz8jmI7k0p/BNIjQ6t4sFERI2sFeH73ybiZl6YQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -372,13 +420,16 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.3.10.tgz", - "integrity": "sha512-B9DszIHkuKtOH2IFeeVkQmSMVUjss9KtHaNXquYYWCjH8IstNgXgx5B0aSBQNr6mn4RcKKRQZXn9Zu1rM3O0/A==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.4.tgz", + "integrity": "sha512-Rpm5/AT1m+DlJmUoYvS4/vXc+0tXJPJ2NQz25TGPyHVF5JrWy75PE0GH6kVxsKtQDuCH4OgzquZq0R4kj/wCVg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -389,13 +440,16 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.3.10.tgz", - "integrity": "sha512-wwAkWD1MR95u+J4LkWP74/vGz+tRrIQvr8kfMMJY8KOQ8+HMVleREOcPYsQX82S7uueco60L58Wc6M1I9WA9Dw==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.4.tgz", + "integrity": "sha512-FNxojWJkL7EajAuzBgoLe0T2G0y112M4lBrDIFl/DomFTx8yqenYOIdsRLNXvOvBBofE8hJi85LjzLmBDpY7/Q==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -406,13 +460,16 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.3.10.tgz", - "integrity": "sha512-QTfHZQh62SDFdYc2nfmZFuTm5yYb4eO1zwfB+90YxUumRCR171tS1GoTX5OD0wrv4UsziMPmrePMtkTnNyYG3g==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.4.tgz", + "integrity": "sha512-aby/PohmmgbShcHqFsZVzG8H6D98+P+A6xRWRrQcLW1pCjabcov5UUlke4UqNQBYTkDQav+jB4zyyDDeKB2GaA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -423,9 +480,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.3.10.tgz", - "integrity": "sha512-o7lYc9n+CfRbHvkjPhm8s9FgbKdYZu5HCcGVMItLjz93EhgJ8AM44W+QckDqLA9MKDNFrR8nPbO4b73VC5kGGQ==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.4.tgz", + "integrity": "sha512-emoXexPZIPAZkz2RKmA95WJUqK3I5MJNYtwEbL5ESciRzhmFMMyekDhNG8hpeOaK+ZGRDxAU4wvGuA5IHQ0h0w==", "cpu": [ "arm64" ], @@ -440,9 +497,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.3.10.tgz", - "integrity": "sha512-pHEFgq7dUEsKnqG9mx9bXihxGI49X+ar+UBrEIj3Wqj3UCZp1rNgV+OoyjFgcXsjCWpuEAF4VJdkZr3TrWdCbQ==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.4.tgz", + "integrity": "sha512-U1jaluLw1qQc2Tx7/CeSoL9N5XcqIH+GWjpUAy1ouB5nVjSCMNO+NNHdY3RAs8zxNurLWAdj6pehQdCA2zyU+Q==", "cpu": [ "x64" ], @@ -457,9 +514,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", - "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", "cpu": [ "ppc64" ], @@ -474,9 +531,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", - "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", "cpu": [ "arm" ], @@ -491,9 +548,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", - "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", "cpu": [ "arm64" ], @@ -508,9 +565,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", - "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", "cpu": [ "x64" ], @@ -525,9 +582,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", - "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", "cpu": [ "arm64" ], @@ -542,9 +599,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", - "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", "cpu": [ "x64" ], @@ -559,9 +616,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", - "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", "cpu": [ "arm64" ], @@ -576,9 +633,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", - "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", "cpu": [ "x64" ], @@ -593,9 +650,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", - "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", "cpu": [ "arm" ], @@ -610,9 +667,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", - "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", "cpu": [ "arm64" ], @@ -627,9 +684,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", - "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", "cpu": [ "ia32" ], @@ -644,9 +701,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", - "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", "cpu": [ "loong64" ], @@ -661,9 +718,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", - "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", "cpu": [ "mips64el" ], @@ -678,9 +735,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", - "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", "cpu": [ "ppc64" ], @@ -695,9 +752,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", - "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", "cpu": [ "riscv64" ], @@ -712,9 +769,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", - "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", "cpu": [ "s390x" ], @@ -729,9 +786,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", - "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", "cpu": [ "x64" ], @@ -746,9 +803,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", - "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", "cpu": [ "arm64" ], @@ -763,9 +820,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", - "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", "cpu": [ "x64" ], @@ -780,9 +837,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", - "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", "cpu": [ "arm64" ], @@ -797,9 +854,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", - "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", "cpu": [ "x64" ], @@ -814,9 +871,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", - "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", "cpu": [ "arm64" ], @@ -831,9 +888,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", - "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", "cpu": [ "x64" ], @@ -848,9 +905,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", - "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", "cpu": [ "arm64" ], @@ -865,9 +922,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", - "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", "cpu": [ "ia32" ], @@ -882,9 +939,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", - "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", "cpu": [ "x64" ], @@ -899,7 +956,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -917,6 +976,8 @@ }, "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", "engines": { @@ -928,6 +989,8 @@ }, "node_modules/@eslint-community/regexpp": { "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", "engines": { @@ -935,13 +998,15 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.21.1", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", - "minimatch": "^3.1.2" + "minimatch": "^3.1.5" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -949,6 +1014,8 @@ }, "node_modules/@eslint/config-helpers": { "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": { @@ -960,6 +1027,8 @@ }, "node_modules/@eslint/core": { "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": { @@ -970,18 +1039,20 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.3", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", + "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.12.4", + "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.2", + "js-yaml": "^4.3.0", + "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, "engines": { @@ -993,6 +1064,8 @@ }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, "license": "MIT", "engines": { @@ -1003,7 +1076,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.39.2", + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", "dev": true, "license": "MIT", "engines": { @@ -1015,6 +1090,8 @@ }, "node_modules/@eslint/object-schema": { "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1023,6 +1100,8 @@ }, "node_modules/@eslint/plugin-kit": { "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": { @@ -1034,27 +1113,47 @@ } }, "node_modules/@humanfs/core": { - "version": "0.19.1", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { - "version": "0.16.7", + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1067,6 +1166,8 @@ }, "node_modules/@humanwhocodes/retry": { "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1079,6 +1180,8 @@ }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "license": "MIT", "dependencies": { @@ -1088,6 +1191,8 @@ }, "node_modules/@jridgewell/remapping": { "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1097,6 +1202,8 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "license": "MIT", "engines": { @@ -1105,11 +1212,15 @@ }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "license": "MIT", "dependencies": { @@ -1119,13 +1230,15 @@ }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", "dev": true, "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", - "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", + "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", "cpu": [ "arm" ], @@ -1137,9 +1250,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", - "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", + "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", "cpu": [ "arm64" ], @@ -1151,9 +1264,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", - "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", + "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", "cpu": [ "arm64" ], @@ -1165,9 +1278,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", - "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", + "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", "cpu": [ "x64" ], @@ -1179,9 +1292,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", - "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", + "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", "cpu": [ "arm64" ], @@ -1193,9 +1306,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", - "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", + "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", "cpu": [ "x64" ], @@ -1207,13 +1320,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", - "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", + "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1221,13 +1337,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", - "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", + "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1235,13 +1354,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", - "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", + "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1249,13 +1371,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", - "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", + "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1263,13 +1388,16 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", - "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", + "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", "cpu": [ "loong64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1277,13 +1405,16 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", - "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", + "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", "cpu": [ "loong64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1291,13 +1422,16 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", - "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", + "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1305,13 +1439,16 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", - "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", + "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1319,13 +1456,16 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", - "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", + "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1333,13 +1473,16 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", - "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", + "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1347,13 +1490,16 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", - "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", + "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1361,13 +1507,16 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", - "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", + "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1375,13 +1524,16 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", - "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", + "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1389,9 +1541,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", - "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", + "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", "cpu": [ "x64" ], @@ -1403,9 +1555,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", - "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", + "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", "cpu": [ "arm64" ], @@ -1417,9 +1569,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", - "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", + "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", "cpu": [ "arm64" ], @@ -1431,9 +1583,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", - "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", + "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", "cpu": [ "ia32" ], @@ -1445,9 +1597,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", - "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", + "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", "cpu": [ "x64" ], @@ -1459,9 +1611,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", - "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", + "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", "cpu": [ "x64" ], @@ -1472,8 +1624,17 @@ "win32" ] }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "license": "MIT", "dependencies": { @@ -1486,6 +1647,8 @@ }, "node_modules/@types/babel__generator": { "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, "license": "MIT", "dependencies": { @@ -1494,6 +1657,8 @@ }, "node_modules/@types/babel__template": { "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "license": "MIT", "dependencies": { @@ -1503,32 +1668,60 @@ }, "node_modules/@types/babel__traverse": { "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.28.2" } }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, "node_modules/@types/debug": { - "version": "4.1.12", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", "license": "MIT", "dependencies": { "@types/ms": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { - "version": "1.0.8", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "license": "MIT" }, "node_modules/@types/estree-jsx": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", "license": "MIT", "dependencies": { "@types/estree": "*" } }, "node_modules/@types/hast": { - "version": "3.0.4", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", "license": "MIT", "dependencies": { "@types/unist": "*" @@ -1536,11 +1729,15 @@ }, "node_modules/@types/json-schema": { "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true, "license": "MIT" }, "node_modules/@types/mdast": { "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", "license": "MIT", "dependencies": { "@types/unist": "*" @@ -1548,27 +1745,37 @@ }, "node_modules/@types/ms": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "license": "MIT" }, "node_modules/@types/node": { - "version": "24.10.4", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.16.0" + "undici-types": "~7.18.0" } }, "node_modules/@types/pako": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz", + "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==", "license": "MIT" }, "node_modules/@types/raf": { "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", + "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", "license": "MIT", "optional": true }, "node_modules/@types/react": { - "version": "19.2.7", + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", + "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -1576,6 +1783,8 @@ }, "node_modules/@types/react-dom": { "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -1584,26 +1793,32 @@ }, "node_modules/@types/trusted-types": { "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "license": "MIT", "optional": true }, "node_modules/@types/unist": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.50.0", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.64.0.tgz", + "integrity": "sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.50.0", - "@typescript-eslint/type-utils": "8.50.0", - "@typescript-eslint/utils": "8.50.0", - "@typescript-eslint/visitor-keys": "8.50.0", - "ignore": "^7.0.0", + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/type-utils": "8.64.0", + "@typescript-eslint/utils": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", + "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1613,13 +1828,15 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.50.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "@typescript-eslint/parser": "^8.64.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", "dev": true, "license": "MIT", "engines": { @@ -1627,15 +1844,17 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.50.0", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.64.0.tgz", + "integrity": "sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.50.0", - "@typescript-eslint/types": "8.50.0", - "@typescript-eslint/typescript-estree": "8.50.0", - "@typescript-eslint/visitor-keys": "8.50.0", - "debug": "^4.3.4" + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1645,18 +1864,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.50.0", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.64.0.tgz", + "integrity": "sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.50.0", - "@typescript-eslint/types": "^8.50.0", - "debug": "^4.3.4" + "@typescript-eslint/tsconfig-utils": "^8.64.0", + "@typescript-eslint/types": "^8.64.0", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1666,16 +1887,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.50.0", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.64.0.tgz", + "integrity": "sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.50.0", - "@typescript-eslint/visitor-keys": "8.50.0" + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1686,7 +1909,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.50.0", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz", + "integrity": "sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==", "dev": true, "license": "MIT", "engines": { @@ -1697,19 +1922,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.50.0", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.64.0.tgz", + "integrity": "sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.50.0", - "@typescript-eslint/typescript-estree": "8.50.0", - "@typescript-eslint/utils": "8.50.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/utils": "8.64.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1719,12 +1946,14 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.50.0", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz", + "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", "dev": true, "license": "MIT", "engines": { @@ -1736,19 +1965,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.50.0", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz", + "integrity": "sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.50.0", - "@typescript-eslint/tsconfig-utils": "8.50.0", - "@typescript-eslint/types": "8.50.0", - "@typescript-eslint/visitor-keys": "8.50.0", - "debug": "^4.3.4", - "minimatch": "^9.0.4", - "semver": "^7.6.0", + "@typescript-eslint/project-service": "8.64.0", + "@typescript-eslint/tsconfig-utils": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.1.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1758,35 +1989,52 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.2" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.3", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, "license": "ISC", "bin": { @@ -1797,14 +2045,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.50.0", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.64.0.tgz", + "integrity": "sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.50.0", - "@typescript-eslint/types": "8.50.0", - "@typescript-eslint/typescript-estree": "8.50.0" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1814,17 +2064,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.50.0", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz", + "integrity": "sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.50.0", - "eslint-visitor-keys": "^4.2.1" + "@typescript-eslint/types": "8.64.0", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1834,12 +2086,29 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@ungap/structured-clone": { - "version": "1.3.0", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==", "license": "ISC" }, "node_modules/@vitejs/plugin-react": { "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", "dev": true, "license": "MIT", "dependencies": { @@ -1857,8 +2126,123 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, + "node_modules/@vitest/expect": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.10", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.10", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.10", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/acorn": { - "version": "8.15.0", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", "dev": true, "license": "MIT", "bin": { @@ -1870,16 +2254,30 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -1895,6 +2293,8 @@ }, "node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "dependencies": { @@ -1909,26 +2309,43 @@ }, "node_modules/argparse": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, "license": "Python-2.0" }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/asynckit": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, "node_modules/axios": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", - "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz", + "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.11", + "follow-redirects": "^1.16.0", "form-data": "^4.0.5", - "proxy-from-env": "^1.1.0" + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" } }, "node_modules/bail": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", "license": "MIT", "funding": { "type": "github", @@ -1937,11 +2354,15 @@ }, "node_modules/balanced-match": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, "license": "MIT" }, "node_modules/base64-arraybuffer": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", "license": "MIT", "optional": true, "engines": { @@ -1949,15 +2370,22 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.9.10", + "version": "2.10.43", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.43.tgz", + "integrity": "sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==", "dev": true, "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/brace-expansion": { - "version": "1.1.12", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -1966,7 +2394,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.1", + "version": "4.28.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.6.tgz", + "integrity": "sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==", "dev": true, "funding": [ { @@ -1984,11 +2414,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" + "baseline-browser-mapping": "^2.10.42", + "caniuse-lite": "^1.0.30001803", + "electron-to-chromium": "^1.5.389", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -1999,6 +2429,8 @@ }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -2010,6 +2442,8 @@ }, "node_modules/callsites": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "license": "MIT", "engines": { @@ -2017,7 +2451,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001760", + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", "dev": true, "funding": [ { @@ -2037,6 +2473,8 @@ }, "node_modules/canvg": { "version": "3.0.11", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz", + "integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==", "license": "MIT", "optional": true, "dependencies": { @@ -2055,14 +2493,28 @@ }, "node_modules/ccount": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -2078,6 +2530,8 @@ }, "node_modules/character-entities": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", "license": "MIT", "funding": { "type": "github", @@ -2086,6 +2540,8 @@ }, "node_modules/character-entities-html4": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", "license": "MIT", "funding": { "type": "github", @@ -2094,6 +2550,8 @@ }, "node_modules/character-entities-legacy": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", "license": "MIT", "funding": { "type": "github", @@ -2102,6 +2560,8 @@ }, "node_modules/character-reference-invalid": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", "license": "MIT", "funding": { "type": "github", @@ -2110,6 +2570,8 @@ }, "node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2121,11 +2583,15 @@ }, "node_modules/color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "license": "MIT" }, "node_modules/combined-stream": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" @@ -2136,6 +2602,8 @@ }, "node_modules/comma-separated-tokens": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", "license": "MIT", "funding": { "type": "github", @@ -2144,11 +2612,15 @@ }, "node_modules/concat-map": { "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, "license": "MIT" }, "node_modules/convert-source-map": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT" }, @@ -2166,7 +2638,9 @@ } }, "node_modules/core-js": { - "version": "3.47.0", + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz", + "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==", "hasInstallScript": true, "license": "MIT", "optional": true, @@ -2177,6 +2651,8 @@ }, "node_modules/cross-spawn": { "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": { @@ -2190,6 +2666,8 @@ }, "node_modules/css-line-break": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", "license": "MIT", "optional": true, "dependencies": { @@ -2198,10 +2676,14 @@ }, "node_modules/csstype": { "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, "node_modules/debug": { "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2216,7 +2698,9 @@ } }, "node_modules/decode-named-character-reference": { - "version": "1.2.0", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", "license": "MIT", "dependencies": { "character-entities": "^2.0.0" @@ -2228,11 +2712,15 @@ }, "node_modules/deep-is": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, "license": "MIT" }, "node_modules/delayed-stream": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "license": "MIT", "engines": { "node": ">=0.4.0" @@ -2240,6 +2728,8 @@ }, "node_modules/dequal": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "license": "MIT", "engines": { "node": ">=6" @@ -2247,6 +2737,8 @@ }, "node_modules/devlop": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", "license": "MIT", "dependencies": { "dequal": "^2.0.0" @@ -2257,9 +2749,9 @@ } }, "node_modules/dompurify": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz", - "integrity": "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==", + "version": "3.4.12", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.12.tgz", + "integrity": "sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==", "license": "(MPL-2.0 OR Apache-2.0)", "optional": true, "optionalDependencies": { @@ -2268,6 +2760,8 @@ }, "node_modules/dotenv": { "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -2278,6 +2772,8 @@ }, "node_modules/dotenv-expand": { "version": "12.0.3", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.3.tgz", + "integrity": "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==", "license": "BSD-2-Clause", "dependencies": { "dotenv": "^16.4.5" @@ -2291,6 +2787,8 @@ }, "node_modules/dunder-proto": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -2302,12 +2800,16 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.267", + "version": "1.5.393", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.393.tgz", + "integrity": "sha512-kiDJdIUawuEIcp9XoICKp1iTYDEbgguIPq526N1Q7jIQDeQ3CqoMx71025PI/7E48Ddtw2HuWsVjY7afEgNxmg==", "dev": true, "license": "ISC" }, "node_modules/es-define-property": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -2315,13 +2817,24 @@ }, "node_modules/es-errors": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", "engines": { "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { - "version": "1.1.1", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -2332,6 +2845,8 @@ }, "node_modules/es-set-tostringtag": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -2344,7 +2859,9 @@ } }, "node_modules/esbuild": { - "version": "0.27.2", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2355,36 +2872,38 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.2", - "@esbuild/android-arm": "0.27.2", - "@esbuild/android-arm64": "0.27.2", - "@esbuild/android-x64": "0.27.2", - "@esbuild/darwin-arm64": "0.27.2", - "@esbuild/darwin-x64": "0.27.2", - "@esbuild/freebsd-arm64": "0.27.2", - "@esbuild/freebsd-x64": "0.27.2", - "@esbuild/linux-arm": "0.27.2", - "@esbuild/linux-arm64": "0.27.2", - "@esbuild/linux-ia32": "0.27.2", - "@esbuild/linux-loong64": "0.27.2", - "@esbuild/linux-mips64el": "0.27.2", - "@esbuild/linux-ppc64": "0.27.2", - "@esbuild/linux-riscv64": "0.27.2", - "@esbuild/linux-s390x": "0.27.2", - "@esbuild/linux-x64": "0.27.2", - "@esbuild/netbsd-arm64": "0.27.2", - "@esbuild/netbsd-x64": "0.27.2", - "@esbuild/openbsd-arm64": "0.27.2", - "@esbuild/openbsd-x64": "0.27.2", - "@esbuild/openharmony-arm64": "0.27.2", - "@esbuild/sunos-x64": "0.27.2", - "@esbuild/win32-arm64": "0.27.2", - "@esbuild/win32-ia32": "0.27.2", - "@esbuild/win32-x64": "0.27.2" + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" } }, "node_modules/escalade": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "license": "MIT", "engines": { @@ -2393,6 +2912,8 @@ }, "node_modules/escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", "engines": { @@ -2403,23 +2924,25 @@ } }, "node_modules/eslint": { - "version": "9.39.2", + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", + "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", "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-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.2", + "@eslint/eslintrc": "^3.3.6", + "@eslint/js": "9.39.5", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "ajv": "^6.12.4", + "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", @@ -2438,7 +2961,7 @@ "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", + "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -2462,6 +2985,8 @@ }, "node_modules/eslint-plugin-react-hooks": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", "dev": true, "license": "MIT", "engines": { @@ -2473,6 +2998,8 @@ }, "node_modules/eslint-plugin-react-refresh": { "version": "0.4.26", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", + "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -2481,6 +3008,8 @@ }, "node_modules/eslint-scope": { "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -2496,6 +3025,8 @@ }, "node_modules/eslint-visitor-keys": { "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2507,6 +3038,8 @@ }, "node_modules/espree": { "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -2522,7 +3055,9 @@ } }, "node_modules/esquery": { - "version": "1.6.0", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -2534,6 +3069,8 @@ }, "node_modules/esrecurse": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -2545,6 +3082,8 @@ }, "node_modules/estraverse": { "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -2553,41 +3092,75 @@ }, "node_modules/estree-util-is-identifier-name": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/extend": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, "node_modules/fast-deep-equal": { "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, "license": "MIT" }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, "license": "MIT" }, "node_modules/fast-png": { "version": "6.4.0", + "resolved": "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz", + "integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==", "license": "MIT", "dependencies": { "@types/pako": "^2.0.3", @@ -2597,6 +3170,8 @@ }, "node_modules/fdir": { "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", "engines": { @@ -2612,11 +3187,15 @@ } }, "node_modules/fflate": { - "version": "0.8.2", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", + "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", "license": "MIT" }, "node_modules/file-entry-cache": { "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2628,6 +3207,8 @@ }, "node_modules/find-up": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { @@ -2643,6 +3224,8 @@ }, "node_modules/flat-cache": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", "dependencies": { @@ -2654,14 +3237,16 @@ } }, "node_modules/flatted": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz", - "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, "node_modules/follow-redirects": { - "version": "1.15.11", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", @@ -2679,14 +3264,16 @@ } }, "node_modules/form-data": { - "version": "4.0.5", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -2709,6 +3296,8 @@ }, "node_modules/function-bind": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2716,6 +3305,8 @@ }, "node_modules/gensync": { "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, "license": "MIT", "engines": { @@ -2724,6 +3315,8 @@ }, "node_modules/get-intrinsic": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -2746,6 +3339,8 @@ }, "node_modules/get-proto": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -2757,6 +3352,8 @@ }, "node_modules/glob-parent": { "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", "dependencies": { @@ -2768,6 +3365,8 @@ }, "node_modules/globals": { "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", "dev": true, "license": "MIT", "engines": { @@ -2779,6 +3378,8 @@ }, "node_modules/gopd": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -2789,6 +3390,8 @@ }, "node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { @@ -2797,6 +3400,8 @@ }, "node_modules/has-symbols": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -2807,6 +3412,8 @@ }, "node_modules/has-tostringtag": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -2819,7 +3426,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -2830,6 +3439,8 @@ }, "node_modules/hast-util-to-jsx-runtime": { "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", "license": "MIT", "dependencies": { "@types/estree": "^1.0.0", @@ -2855,6 +3466,8 @@ }, "node_modules/hast-util-whitespace": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0" @@ -2866,10 +3479,14 @@ }, "node_modules/html-to-image": { "version": "1.11.13", + "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.13.tgz", + "integrity": "sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg==", "license": "MIT" }, "node_modules/html-url-attributes": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", "license": "MIT", "funding": { "type": "opencollective", @@ -2878,6 +3495,8 @@ }, "node_modules/html2canvas": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", "license": "MIT", "optional": true, "dependencies": { @@ -2888,8 +3507,23 @@ "node": ">=8.0.0" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/ignore": { "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "engines": { @@ -2898,6 +3532,8 @@ }, "node_modules/import-fresh": { "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2913,6 +3549,8 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", "engines": { @@ -2921,14 +3559,20 @@ }, "node_modules/inline-style-parser": { "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", "license": "MIT" }, "node_modules/iobuffer": { "version": "5.4.0", + "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz", + "integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==", "license": "MIT" }, "node_modules/is-alphabetical": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", "license": "MIT", "funding": { "type": "github", @@ -2937,6 +3581,8 @@ }, "node_modules/is-alphanumerical": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", "license": "MIT", "dependencies": { "is-alphabetical": "^2.0.0", @@ -2949,6 +3595,8 @@ }, "node_modules/is-decimal": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", "license": "MIT", "funding": { "type": "github", @@ -2957,6 +3605,8 @@ }, "node_modules/is-extglob": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", "engines": { @@ -2965,6 +3615,8 @@ }, "node_modules/is-glob": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", "dependencies": { @@ -2976,6 +3628,8 @@ }, "node_modules/is-hexadecimal": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", "license": "MIT", "funding": { "type": "github", @@ -2984,6 +3638,8 @@ }, "node_modules/is-plain-obj": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "license": "MIT", "engines": { "node": ">=12" @@ -2994,17 +3650,33 @@ }, "node_modules/isexe": { "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/js-tokens": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true, "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.1", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -3015,6 +3687,8 @@ }, "node_modules/jsesc": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "license": "MIT", "bin": { @@ -3026,21 +3700,29 @@ }, "node_modules/json-buffer": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, "license": "MIT" }, "node_modules/json5": { "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "license": "MIT", "bin": { @@ -3069,6 +3751,8 @@ }, "node_modules/keyv": { "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", "dependencies": { @@ -3077,6 +3761,8 @@ }, "node_modules/levn": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3089,6 +3775,8 @@ }, "node_modules/locate-path": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { @@ -3103,11 +3791,15 @@ }, "node_modules/lodash.merge": { "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, "license": "MIT" }, "node_modules/longest-streak": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", "license": "MIT", "funding": { "type": "github", @@ -3116,14 +3808,28 @@ }, "node_modules/lru-cache": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "license": "ISC", "dependencies": { "yallist": "^3.0.2" } }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/markdown-table": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", "license": "MIT", "funding": { "type": "github", @@ -3132,6 +3838,8 @@ }, "node_modules/math-intrinsics": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -3139,6 +3847,8 @@ }, "node_modules/mdast-util-find-and-replace": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -3153,6 +3863,8 @@ }, "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "license": "MIT", "engines": { "node": ">=12" @@ -3162,7 +3874,9 @@ } }, "node_modules/mdast-util-from-markdown": { - "version": "2.0.2", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -3185,6 +3899,8 @@ }, "node_modules/mdast-util-gfm": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", "license": "MIT", "dependencies": { "mdast-util-from-markdown": "^2.0.0", @@ -3202,6 +3918,8 @@ }, "node_modules/mdast-util-gfm-autolink-literal": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -3217,6 +3935,8 @@ }, "node_modules/mdast-util-gfm-footnote": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -3232,6 +3952,8 @@ }, "node_modules/mdast-util-gfm-strikethrough": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -3245,6 +3967,8 @@ }, "node_modules/mdast-util-gfm-table": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -3260,6 +3984,8 @@ }, "node_modules/mdast-util-gfm-task-list-item": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -3274,6 +4000,8 @@ }, "node_modules/mdast-util-mdx-expression": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", "license": "MIT", "dependencies": { "@types/estree-jsx": "^1.0.0", @@ -3290,6 +4018,8 @@ }, "node_modules/mdast-util-mdx-jsx": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", "license": "MIT", "dependencies": { "@types/estree-jsx": "^1.0.0", @@ -3312,6 +4042,8 @@ }, "node_modules/mdast-util-mdxjs-esm": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", "license": "MIT", "dependencies": { "@types/estree-jsx": "^1.0.0", @@ -3328,6 +4060,8 @@ }, "node_modules/mdast-util-phrasing": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -3340,6 +4074,8 @@ }, "node_modules/mdast-util-to-hast": { "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -3359,6 +4095,8 @@ }, "node_modules/mdast-util-to-markdown": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -3378,6 +4116,8 @@ }, "node_modules/mdast-util-to-string": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0" @@ -3389,6 +4129,8 @@ }, "node_modules/micromark": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", "funding": [ { "type": "GitHub Sponsors", @@ -3422,6 +4164,8 @@ }, "node_modules/micromark-core-commonmark": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", "funding": [ { "type": "GitHub Sponsors", @@ -3454,6 +4198,8 @@ }, "node_modules/micromark-extension-gfm": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", "license": "MIT", "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", @@ -3472,6 +4218,8 @@ }, "node_modules/micromark-extension-gfm-autolink-literal": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", "license": "MIT", "dependencies": { "micromark-util-character": "^2.0.0", @@ -3486,6 +4234,8 @@ }, "node_modules/micromark-extension-gfm-footnote": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", "license": "MIT", "dependencies": { "devlop": "^1.0.0", @@ -3504,6 +4254,8 @@ }, "node_modules/micromark-extension-gfm-strikethrough": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", "license": "MIT", "dependencies": { "devlop": "^1.0.0", @@ -3520,6 +4272,8 @@ }, "node_modules/micromark-extension-gfm-table": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", "license": "MIT", "dependencies": { "devlop": "^1.0.0", @@ -3535,6 +4289,8 @@ }, "node_modules/micromark-extension-gfm-tagfilter": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", "license": "MIT", "dependencies": { "micromark-util-types": "^2.0.0" @@ -3546,6 +4302,8 @@ }, "node_modules/micromark-extension-gfm-task-list-item": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", "license": "MIT", "dependencies": { "devlop": "^1.0.0", @@ -3561,6 +4319,8 @@ }, "node_modules/micromark-factory-destination": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", "funding": [ { "type": "GitHub Sponsors", @@ -3580,6 +4340,8 @@ }, "node_modules/micromark-factory-label": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", "funding": [ { "type": "GitHub Sponsors", @@ -3600,6 +4362,8 @@ }, "node_modules/micromark-factory-space": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", "funding": [ { "type": "GitHub Sponsors", @@ -3618,6 +4382,8 @@ }, "node_modules/micromark-factory-title": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", "funding": [ { "type": "GitHub Sponsors", @@ -3638,6 +4404,8 @@ }, "node_modules/micromark-factory-whitespace": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", "funding": [ { "type": "GitHub Sponsors", @@ -3658,6 +4426,8 @@ }, "node_modules/micromark-util-character": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", "funding": [ { "type": "GitHub Sponsors", @@ -3676,6 +4446,8 @@ }, "node_modules/micromark-util-chunked": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", "funding": [ { "type": "GitHub Sponsors", @@ -3693,6 +4465,8 @@ }, "node_modules/micromark-util-classify-character": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", "funding": [ { "type": "GitHub Sponsors", @@ -3712,6 +4486,8 @@ }, "node_modules/micromark-util-combine-extensions": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", "funding": [ { "type": "GitHub Sponsors", @@ -3730,6 +4506,8 @@ }, "node_modules/micromark-util-decode-numeric-character-reference": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", "funding": [ { "type": "GitHub Sponsors", @@ -3747,6 +4525,8 @@ }, "node_modules/micromark-util-decode-string": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", "funding": [ { "type": "GitHub Sponsors", @@ -3767,6 +4547,8 @@ }, "node_modules/micromark-util-encode": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", "funding": [ { "type": "GitHub Sponsors", @@ -3781,6 +4563,8 @@ }, "node_modules/micromark-util-html-tag-name": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", "funding": [ { "type": "GitHub Sponsors", @@ -3795,6 +4579,8 @@ }, "node_modules/micromark-util-normalize-identifier": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", "funding": [ { "type": "GitHub Sponsors", @@ -3812,6 +4598,8 @@ }, "node_modules/micromark-util-resolve-all": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", "funding": [ { "type": "GitHub Sponsors", @@ -3829,6 +4617,8 @@ }, "node_modules/micromark-util-sanitize-uri": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", "funding": [ { "type": "GitHub Sponsors", @@ -3848,6 +4638,8 @@ }, "node_modules/micromark-util-subtokenize": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", "funding": [ { "type": "GitHub Sponsors", @@ -3868,6 +4660,8 @@ }, "node_modules/micromark-util-symbol": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", "funding": [ { "type": "GitHub Sponsors", @@ -3882,6 +4676,8 @@ }, "node_modules/micromark-util-types": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", "funding": [ { "type": "GitHub Sponsors", @@ -3896,6 +4692,8 @@ }, "node_modules/mime-db": { "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -3903,6 +4701,8 @@ }, "node_modules/mime-types": { "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -3926,10 +4726,14 @@ }, "node_modules/ms": { "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.11", + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", "dev": true, "funding": [ { @@ -3947,20 +4751,45 @@ }, "node_modules/natural-compare": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.27", + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/normalize-wheel": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/normalize-wheel/-/normalize-wheel-1.0.1.tgz", + "integrity": "sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==", "license": "BSD-3-Clause" }, + "node_modules/obug": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/optionator": { "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", "dependencies": { @@ -3977,6 +4806,8 @@ }, "node_modules/p-limit": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3991,6 +4822,8 @@ }, "node_modules/p-locate": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { @@ -4004,11 +4837,25 @@ } }, "node_modules/pako": { - "version": "2.1.0", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.2.0.tgz", + "integrity": "sha512-zJq6RP/5q+TO2OpFV3FHzlPnFjmkb7Nc99a5SNjJE+uu/PkpChs+NIZSSzbBoD+6kjiISXjfYdwj1ZRQ81dz/w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "(MIT AND Zlib)" }, "node_modules/parent-module": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", "dependencies": { @@ -4020,6 +4867,8 @@ }, "node_modules/parse-entities": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", @@ -4037,10 +4886,14 @@ }, "node_modules/parse-entities/node_modules/@types/unist": { "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", "license": "MIT" }, "node_modules/path-exists": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", "engines": { @@ -4049,24 +4902,39 @@ }, "node_modules/path-key": { "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" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/performance-now": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", "license": "MIT", "optional": true }, "node_modules/picocolors": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true, "license": "MIT", "engines": { @@ -4077,7 +4945,9 @@ } }, "node_modules/postcss": { - "version": "8.5.6", + "version": "8.5.19", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz", + "integrity": "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==", "dev": true, "funding": [ { @@ -4095,7 +4965,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -4105,6 +4975,8 @@ }, "node_modules/prelude-ls": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", "engines": { @@ -4112,7 +4984,9 @@ } }, "node_modules/property-information": { - "version": "7.1.0", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", "license": "MIT", "funding": { "type": "github", @@ -4120,11 +4994,18 @@ } }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "license": "MIT" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/punycode": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", "engines": { @@ -4133,6 +5014,8 @@ }, "node_modules/raf": { "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", "license": "MIT", "optional": true, "dependencies": { @@ -4140,24 +5023,30 @@ } }, "node_modules/react": { - "version": "19.2.3", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", + "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.2.3", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", + "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", "license": "MIT", "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.2.3" + "react": "^19.2.7" } }, "node_modules/react-easy-crop": { - "version": "5.5.6", + "version": "5.5.7", + "resolved": "https://registry.npmjs.org/react-easy-crop/-/react-easy-crop-5.5.7.tgz", + "integrity": "sha512-kYo4NtMeXFQB7h1U+h5yhUkE46WQbQdq7if54uDlbMdZHdRgNehfvaFrXnFw5NR1PNoUOJIfTwLnWmEx/MaZnA==", "license": "MIT", "dependencies": { "normalize-wheel": "^1.0.1", @@ -4175,7 +5064,9 @@ "license": "MIT" }, "node_modules/react-icons": { - "version": "5.5.0", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.7.0.tgz", + "integrity": "sha512-LBLy340Rzqy6+/yVhZKT3B/QpP1BZaesGqasf09HPOBzRarcDIFH0WwXlXQfE7q7ipxK4MSiC5DIBWURCny6fw==", "license": "MIT", "peerDependencies": { "react": "*" @@ -4183,6 +5074,8 @@ }, "node_modules/react-markdown": { "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -4208,6 +5101,8 @@ }, "node_modules/react-refresh": { "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", "dev": true, "license": "MIT", "engines": { @@ -4215,9 +5110,9 @@ } }, "node_modules/react-router": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.1.tgz", - "integrity": "sha512-td+xP4X2/6BJvZoX6xw++A2DdEi++YypA69bJUV5oVvqf6/9/9nNlD70YO1e9d3MyamJEBQFEzk6mbfDYbqrSA==", + "version": "7.18.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.1.tgz", + "integrity": "sha512-GDLgg3i3uM0aeJO3Fm+TCS+sDQ7gu12T6x0qdTEzcwqEfleci7JwugVNIF3U//0FWKnJT7ptG+20B2jfDqnZAg==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -4237,12 +5132,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.1.tgz", - "integrity": "sha512-UJnV3Rxc5TgUPJt2KJpo1Jpy0OKQr0AjgbZzBFjaPJcFOb2Y8jA5H3LT8HUJAiRLlWrEXWHbF1Z4SCZaQjWDHw==", + "version": "7.18.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.1.tgz", + "integrity": "sha512-KaZh+X/6UtEp28x51AUYZDMg9NGoz2ja3dNHa+ta/tk40vCzKhQ/RypCWBMLbmDr6//E24Vv5uPsrqXFozdkAg==", "license": "MIT", "dependencies": { - "react-router": "7.13.1" + "react-router": "7.18.1" }, "engines": { "node": ">=20.0.0" @@ -4254,11 +5149,15 @@ }, "node_modules/regenerator-runtime": { "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "license": "MIT", "optional": true }, "node_modules/remark-gfm": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -4275,6 +5174,8 @@ }, "node_modules/remark-parse": { "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -4289,6 +5190,8 @@ }, "node_modules/remark-rehype": { "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -4304,6 +5207,8 @@ }, "node_modules/remark-stringify": { "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", "license": "MIT", "dependencies": { "@types/mdast": "^4.0.0", @@ -4317,6 +5222,8 @@ }, "node_modules/resolve-from": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", "engines": { @@ -4325,6 +5232,8 @@ }, "node_modules/rgbcolor": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", "license": "MIT OR SEE LICENSE IN FEEL-FREE.md", "optional": true, "engines": { @@ -4332,13 +5241,13 @@ } }, "node_modules/rollup": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", - "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", + "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" + "@types/estree": "1.0.9" }, "bin": { "rollup": "dist/bin/rollup" @@ -4348,40 +5257,44 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.59.0", - "@rollup/rollup-android-arm64": "4.59.0", - "@rollup/rollup-darwin-arm64": "4.59.0", - "@rollup/rollup-darwin-x64": "4.59.0", - "@rollup/rollup-freebsd-arm64": "4.59.0", - "@rollup/rollup-freebsd-x64": "4.59.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", - "@rollup/rollup-linux-arm-musleabihf": "4.59.0", - "@rollup/rollup-linux-arm64-gnu": "4.59.0", - "@rollup/rollup-linux-arm64-musl": "4.59.0", - "@rollup/rollup-linux-loong64-gnu": "4.59.0", - "@rollup/rollup-linux-loong64-musl": "4.59.0", - "@rollup/rollup-linux-ppc64-gnu": "4.59.0", - "@rollup/rollup-linux-ppc64-musl": "4.59.0", - "@rollup/rollup-linux-riscv64-gnu": "4.59.0", - "@rollup/rollup-linux-riscv64-musl": "4.59.0", - "@rollup/rollup-linux-s390x-gnu": "4.59.0", - "@rollup/rollup-linux-x64-gnu": "4.59.0", - "@rollup/rollup-linux-x64-musl": "4.59.0", - "@rollup/rollup-openbsd-x64": "4.59.0", - "@rollup/rollup-openharmony-arm64": "4.59.0", - "@rollup/rollup-win32-arm64-msvc": "4.59.0", - "@rollup/rollup-win32-ia32-msvc": "4.59.0", - "@rollup/rollup-win32-x64-gnu": "4.59.0", - "@rollup/rollup-win32-x64-msvc": "4.59.0", + "@rollup/rollup-android-arm-eabi": "4.62.2", + "@rollup/rollup-android-arm64": "4.62.2", + "@rollup/rollup-darwin-arm64": "4.62.2", + "@rollup/rollup-darwin-x64": "4.62.2", + "@rollup/rollup-freebsd-arm64": "4.62.2", + "@rollup/rollup-freebsd-x64": "4.62.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", + "@rollup/rollup-linux-arm-musleabihf": "4.62.2", + "@rollup/rollup-linux-arm64-gnu": "4.62.2", + "@rollup/rollup-linux-arm64-musl": "4.62.2", + "@rollup/rollup-linux-loong64-gnu": "4.62.2", + "@rollup/rollup-linux-loong64-musl": "4.62.2", + "@rollup/rollup-linux-ppc64-gnu": "4.62.2", + "@rollup/rollup-linux-ppc64-musl": "4.62.2", + "@rollup/rollup-linux-riscv64-gnu": "4.62.2", + "@rollup/rollup-linux-riscv64-musl": "4.62.2", + "@rollup/rollup-linux-s390x-gnu": "4.62.2", + "@rollup/rollup-linux-x64-gnu": "4.62.2", + "@rollup/rollup-linux-x64-musl": "4.62.2", + "@rollup/rollup-openbsd-x64": "4.62.2", + "@rollup/rollup-openharmony-arm64": "4.62.2", + "@rollup/rollup-win32-arm64-msvc": "4.62.2", + "@rollup/rollup-win32-ia32-msvc": "4.62.2", + "@rollup/rollup-win32-x64-gnu": "4.62.2", + "@rollup/rollup-win32-x64-msvc": "4.62.2", "fsevents": "~2.3.2" } }, "node_modules/scheduler": { "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, "node_modules/semver": { "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { @@ -4396,6 +5309,8 @@ }, "node_modules/shebang-command": { "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": { @@ -4407,14 +5322,25 @@ }, "node_modules/shebang-regex": { "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" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/source-map-js": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -4423,22 +5349,42 @@ }, "node_modules/space-separated-tokens": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, "node_modules/stackblur-canvas": { "version": "2.7.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", + "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", "license": "MIT", "optional": true, "engines": { "node": ">=0.1.14" } }, + "node_modules/std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "dev": true, + "license": "MIT" + }, "node_modules/stringify-entities": { "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", "license": "MIT", "dependencies": { "character-entities-html4": "^2.0.0", @@ -4451,6 +5397,8 @@ }, "node_modules/strip-json-comments": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", "engines": { @@ -4462,6 +5410,8 @@ }, "node_modules/style-to-js": { "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", "license": "MIT", "dependencies": { "style-to-object": "1.0.14" @@ -4469,6 +5419,8 @@ }, "node_modules/style-to-object": { "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", "license": "MIT", "dependencies": { "inline-style-parser": "0.2.7" @@ -4476,6 +5428,8 @@ }, "node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { @@ -4487,6 +5441,8 @@ }, "node_modules/svg-pathdata": { "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", "license": "MIT", "optional": true, "engines": { @@ -4495,19 +5451,40 @@ }, "node_modules/text-segmentation": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", "license": "MIT", "optional": true, "dependencies": { "utrie": "^1.0.2" } }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { - "version": "0.2.15", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -4516,8 +5493,20 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/trim-lines": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", "license": "MIT", "funding": { "type": "github", @@ -4526,6 +5515,8 @@ }, "node_modules/trough": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", "license": "MIT", "funding": { "type": "github", @@ -4533,7 +5524,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.1.0", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", "engines": { @@ -4545,10 +5538,14 @@ }, "node_modules/tslib": { "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, "node_modules/type-check": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", "dependencies": { @@ -4560,6 +5557,8 @@ }, "node_modules/typescript": { "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -4571,14 +5570,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.50.0", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.64.0.tgz", + "integrity": "sha512-0qg+pDNMnqYzqH9AnNK+39tejHvsShUOUUoRUgtnTGE7QuMZhiFDnozq8nHJVq+Wae6NMLKNWLg5WmkcC/ndyQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.50.0", - "@typescript-eslint/parser": "8.50.0", - "@typescript-eslint/typescript-estree": "8.50.0", - "@typescript-eslint/utils": "8.50.0" + "@typescript-eslint/eslint-plugin": "8.64.0", + "@typescript-eslint/parser": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/utils": "8.64.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4588,17 +5589,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/undici-types": { - "version": "7.16.0", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", "dev": true, "license": "MIT" }, "node_modules/unified": { "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -4616,6 +5621,8 @@ }, "node_modules/unist-util-is": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" @@ -4627,6 +5634,8 @@ }, "node_modules/unist-util-position": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" @@ -4638,6 +5647,8 @@ }, "node_modules/unist-util-stringify-position": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" @@ -4648,7 +5659,9 @@ } }, "node_modules/unist-util-visit": { - "version": "5.0.0", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -4662,6 +5675,8 @@ }, "node_modules/unist-util-visit-parents": { "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -4674,6 +5689,8 @@ }, "node_modules/update-browserslist-db": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -4703,6 +5720,8 @@ }, "node_modules/uri-js": { "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -4711,6 +5730,8 @@ }, "node_modules/utrie": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", "license": "MIT", "optional": true, "dependencies": { @@ -4719,6 +5740,8 @@ }, "node_modules/vfile": { "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -4731,6 +5754,8 @@ }, "node_modules/vfile-message": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -4742,11 +5767,13 @@ } }, "node_modules/vite": { - "version": "7.3.0", + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.27.0", + "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", @@ -4814,8 +5841,100 @@ } } }, + "node_modules/vitest": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, "node_modules/which": { "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": { @@ -4828,8 +5947,27 @@ "node": ">= 8" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", "engines": { @@ -4838,11 +5976,15 @@ }, "node_modules/yallist": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, "license": "ISC" }, "node_modules/yocto-queue": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", "engines": { @@ -4854,6 +5996,8 @@ }, "node_modules/zwitch": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", "license": "MIT", "funding": { "type": "github", diff --git a/frontend/package-lock.json.backup b/frontend/package-lock.json.backup new file mode 100644 index 0000000..ebd7a22 --- /dev/null +++ b/frontend/package-lock.json.backup @@ -0,0 +1,5336 @@ +{ + "name": "tz-fabric", + "version": "1.5.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "tz-fabric", + "version": "1.5.1", + "license": "MIT", + "dependencies": { + "axios": "^1.18.1", + "dotenv-expand": "^12.0.3", + "html-to-image": "^1.11.13", + "jspdf": "^4.2.1", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-easy-crop": "^5.5.6", + "react-ga4": "^3.0.1", + "react-icons": "^5.5.0", + "react-markdown": "^10.1.0", + "react-router-dom": "^7.18.1", + "remark-gfm": "^4.0.1" + }, + "devDependencies": { + "@biomejs/biome": "^2.0.2", + "@eslint/js": "^9.30.1", + "@types/node": "^24.10.1", + "@types/react": "^19.1.9", + "@types/react-dom": "^19.1.7", + "@vitejs/plugin-react": "^4.6.0", + "eslint": "^9.30.1", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.3.0", + "typescript": "~5.8.3", + "typescript-eslint": "^8.35.1", + "vite": "^7.3.6", + "vitest": "^4.1.10" + }, + "funding": { + "type": "individual", + "url": "https://github.com/xkeshav" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@biomejs/biome": { + "version": "2.3.10", + "dev": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.3.10", + "@biomejs/cli-darwin-x64": "2.3.10", + "@biomejs/cli-linux-arm64": "2.3.10", + "@biomejs/cli-linux-arm64-musl": "2.3.10", + "@biomejs/cli-linux-x64": "2.3.10", + "@biomejs/cli-linux-x64-musl": "2.3.10", + "@biomejs/cli-win32-arm64": "2.3.10", + "@biomejs/cli-win32-x64": "2.3.10" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.3.10.tgz", + "integrity": "sha512-M6xUjtCVnNGFfK7HMNKa593nb7fwNm43fq1Mt71kpLpb+4mE7odO8W/oWVDyBVO4ackhresy1ZYO7OJcVo/B7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.3.10.tgz", + "integrity": "sha512-Vae7+V6t/Avr8tVbFNjnFSTKZogZHFYl7MMH62P/J1kZtr0tyRQ9Fe0onjqjS2Ek9lmNLmZc/VR5uSekh+p1fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.3.10.tgz", + "integrity": "sha512-hhPw2V3/EpHKsileVOFynuWiKRgFEV48cLe0eA+G2wO4SzlwEhLEB9LhlSrVeu2mtSn205W283LkX7Fh48CaxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.3.10.tgz", + "integrity": "sha512-B9DszIHkuKtOH2IFeeVkQmSMVUjss9KtHaNXquYYWCjH8IstNgXgx5B0aSBQNr6mn4RcKKRQZXn9Zu1rM3O0/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.3.10.tgz", + "integrity": "sha512-wwAkWD1MR95u+J4LkWP74/vGz+tRrIQvr8kfMMJY8KOQ8+HMVleREOcPYsQX82S7uueco60L58Wc6M1I9WA9Dw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.3.10.tgz", + "integrity": "sha512-QTfHZQh62SDFdYc2nfmZFuTm5yYb4eO1zwfB+90YxUumRCR171tS1GoTX5OD0wrv4UsziMPmrePMtkTnNyYG3g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.3.10.tgz", + "integrity": "sha512-o7lYc9n+CfRbHvkjPhm8s9FgbKdYZu5HCcGVMItLjz93EhgJ8AM44W+QckDqLA9MKDNFrR8nPbO4b73VC5kGGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.3.10.tgz", + "integrity": "sha512-pHEFgq7dUEsKnqG9mx9bXihxGI49X+ar+UBrEIj3Wqj3UCZp1rNgV+OoyjFgcXsjCWpuEAF4VJdkZr3TrWdCbQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.2", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/pako": { + "version": "2.0.4", + "license": "MIT" + }, + "node_modules/@types/raf": { + "version": "3.4.3", + "license": "MIT", + "optional": true + }, + "node_modules/@types/react": { + "version": "19.2.7", + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "license": "MIT", + "optional": true + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.50.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.50.0", + "@typescript-eslint/type-utils": "8.50.0", + "@typescript-eslint/utils": "8.50.0", + "@typescript-eslint/visitor-keys": "8.50.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.50.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.50.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.50.0", + "@typescript-eslint/types": "8.50.0", + "@typescript-eslint/typescript-estree": "8.50.0", + "@typescript-eslint/visitor-keys": "8.50.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.50.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.50.0", + "@typescript-eslint/types": "^8.50.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.50.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.50.0", + "@typescript-eslint/visitor-keys": "8.50.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.50.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.50.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.50.0", + "@typescript-eslint/typescript-estree": "8.50.0", + "@typescript-eslint/utils": "8.50.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.50.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.50.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.50.0", + "@typescript-eslint/tsconfig-utils": "8.50.0", + "@typescript-eslint/types": "8.50.0", + "@typescript-eslint/visitor-keys": "8.50.0", + "debug": "^4.3.4", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.1.2", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.3", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.50.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.50.0", + "@typescript-eslint/types": "8.50.0", + "@typescript-eslint/typescript-estree": "8.50.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.50.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.50.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "license": "ISC" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.10", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.10", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.10", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.10", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.10", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.10", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.10", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.10", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.10", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.10", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.18.1", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/axios/-/axios-1.18.1.tgz", + "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.43", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/baseline-browser-mapping/-/baseline-browser-mapping-2.10.43.tgz", + "integrity": "sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.28.6", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/browserslist/-/browserslist-4.28.6.tgz", + "integrity": "sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.42", + "caniuse-lite": "^1.0.30001803", + "electron-to-chromium": "^1.5.389", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001805", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/caniuse-lite/-/caniuse-lite-1.0.30001805.tgz", + "integrity": "sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/canvg": { + "version": "3.0.11", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@types/raf": "^3.4.0", + "core-js": "^3.8.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.7", + "rgbcolor": "^1.0.1", + "stackblur-canvas": "^2.0.0", + "svg-pathdata": "^6.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ccount": { + "version": "2.0.1", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/core-js": { + "version": "3.47.0", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-line-break": { + "version": "2.1.0", + "license": "MIT", + "optional": true, + "dependencies": { + "utrie": "^1.0.2" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.2.0", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dompurify": { + "version": "3.4.12", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/dompurify/-/dompurify-3.4.12.tgz", + "integrity": "sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==", + "optional": true, + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "12.0.3", + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.389", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/electron-to-chromium/-/electron-to-chromium-1.5.389.tgz", + "integrity": "sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==", + "dev": true, + "license": "ISC" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "dev": true + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.27.2", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.2", + "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.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.2", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.26", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-png": { + "version": "6.4.0", + "license": "MIT", + "dependencies": { + "@types/pako": "^2.0.3", + "iobuffer": "^5.3.2", + "pako": "^2.1.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-to-image": { + "version": "1.11.13", + "license": "MIT" + }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html2canvas": { + "version": "1.4.1", + "license": "MIT", + "optional": true, + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "license": "MIT" + }, + "node_modules/iobuffer": { + "version": "5.4.0", + "license": "MIT" + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.3.0", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jspdf": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-4.2.1.tgz", + "integrity": "sha512-YyAXyvnmjTbR4bHQRLzex3CuINCDlQnBqoSYyjJwTP2x9jDLuKDzy7aKUl0hgx3uhcl7xzg32agn5vlie6HIlQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.6", + "fast-png": "^6.2.0", + "fflate": "^0.8.1" + }, + "optionalDependencies": { + "canvg": "^3.0.11", + "core-js": "^3.6.0", + "dompurify": "^3.3.1", + "html2canvas": "^1.0.0-rc.5" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.16", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.51", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-wheel": { + "version": "1.0.1", + "license": "BSD-3-Clause" + }, + "node_modules/obug": { + "version": "2.1.3", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/obug/-/obug-2.1.3.tgz", + "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pako": { + "version": "2.1.0", + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/performance-now": { + "version": "2.1.0", + "license": "MIT", + "optional": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.19", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/postcss/-/postcss-8.5.19.tgz", + "integrity": "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/property-information": { + "version": "7.1.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/raf": { + "version": "3.4.1", + "license": "MIT", + "optional": true, + "dependencies": { + "performance-now": "^2.1.0" + } + }, + "node_modules/react": { + "version": "19.2.3", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.3", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.3" + } + }, + "node_modules/react-easy-crop": { + "version": "5.5.6", + "license": "MIT", + "dependencies": { + "normalize-wheel": "^1.0.1", + "tslib": "^2.0.1" + }, + "peerDependencies": { + "react": ">=16.4.0", + "react-dom": ">=16.4.0" + } + }, + "node_modules/react-ga4": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/react-ga4/-/react-ga4-3.0.1.tgz", + "integrity": "sha512-GyCc01bSheWXjzGDyHsXMOqk/SP5Cf/JrcJTg4hcpKx4eeSwaJKpJUc+ipF4ffLTZkmabmf3ZGBv4OKHTXNXyA==", + "license": "MIT" + }, + "node_modules/react-icons": { + "version": "5.5.0", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-markdown": { + "version": "10.1.0", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.18.1", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/react-router/-/react-router-7.18.1.tgz", + "integrity": "sha512-GDLgg3i3uM0aeJO3Fm+TCS+sDQ7gu12T6x0qdTEzcwqEfleci7JwugVNIF3U//0FWKnJT7ptG+20B2jfDqnZAg==", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.18.1", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/react-router-dom/-/react-router-dom-7.18.1.tgz", + "integrity": "sha512-KaZh+X/6UtEp28x51AUYZDMg9NGoz2ja3dNHa+ta/tk40vCzKhQ/RypCWBMLbmDr6//E24Vv5uPsrqXFozdkAg==", + "dependencies": { + "react-router": "7.18.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "license": "MIT", + "optional": true + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rgbcolor": { + "version": "1.0.1", + "license": "MIT OR SEE LICENSE IN FEEL-FREE.md", + "optional": true, + "engines": { + "node": ">= 0.8.15" + } + }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "node_modules/stackblur-canvas": { + "version": "2.7.0", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.14" + } + }, + "node_modules/std-env": { + "version": "4.2.0", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "dev": true + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/svg-pathdata": { + "version": "6.0.3", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "license": "MIT", + "optional": true, + "dependencies": { + "utrie": "^1.0.2" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.2.4", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.50.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.50.0", + "@typescript-eslint/parser": "8.50.0", + "@typescript-eslint/typescript-estree": "8.50.0", + "@typescript-eslint/utils": "8.50.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "dev": true, + "license": "MIT" + }, + "node_modules/unified": { + "version": "11.0.5", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utrie": { + "version": "1.0.2", + "license": "MIT", + "optional": true, + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "7.3.6", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.1.10", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://packages.applied-caas-gateway1.internal.api.openai.org/artifactory/api/npm/npm-public/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json index d4dc678..c7694ac 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -54,14 +54,19 @@ "scripts": { "dev": "vite", "build": "tsc -b && vite build", - "lint": "biome lint .", + "lint": "npm run lint:biome && npm run lint:eslint", "fix": " biome format . --write", "preview": "vite preview", - "clean": "rm -rf dist rm -rf build", - "nuke": "rm -rf node_modules && rm -f package-lock.json && npm run clean" + "clean": "node -e \"const fs=require('node:fs'); for (const path of ['dist','build']) fs.rmSync(path,{recursive:true,force:true});\"", + "nuke": "node -e \"const fs=require('node:fs'); fs.rmSync('node_modules',{recursive:true,force:true});\" && npm run clean", + "lint:biome": "biome lint .", + "lint:eslint": "eslint .", + "test": "vitest run", + "test:watch": "vitest", + "start": "vite preview --host 0.0.0.0 --port 5173" }, "dependencies": { - "axios": "^1.11.0", + "axios": "^1.18.1", "dotenv-expand": "^12.0.3", "html-to-image": "^1.11.13", "jspdf": "^4.2.1", @@ -71,11 +76,11 @@ "react-ga4": "^3.0.1", "react-icons": "^5.5.0", "react-markdown": "^10.1.0", - "react-router-dom": "^7.7.1", + "react-router-dom": "^7.18.1", "remark-gfm": "^4.0.1" }, "devDependencies": { - "@biomejs/biome": "^2.0.2", + "@biomejs/biome": "2.5.4", "@eslint/js": "^9.30.1", "@types/node": "^24.10.1", "@types/react": "^19.1.9", @@ -87,6 +92,11 @@ "globals": "^16.3.0", "typescript": "~5.8.3", "typescript-eslint": "^8.35.1", - "vite": "^7.0.4" + "vite": "^7.3.6", + "vitest": "^4.1.10" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0", + "npm": ">=10" } } diff --git a/frontend/src/App.css b/frontend/src/App.css index 03626c2..49d8986 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -17,7 +17,8 @@ #root { height: 100%; margin: 0; - font-family: Inter, "Segoe UI", Roboto, system-ui, -apple-system, Arial, sans-serif; + font-family: + Inter, "Segoe UI", Roboto, system-ui, -apple-system, Arial, sans-serif; } .app-wrapper { @@ -29,14 +30,16 @@ } .app-wrapper.upload-bg { - background-image: url('/fabric_background.png'); - background-image: image-set(url('/fabric_background.webp') type('image/webp'), url('/fabric_background.png') type('image/png')); + background-image: url("/fabric_background.png"); + background-image: image-set( + url("/fabric_background.webp") type("image/webp"), + url("/fabric_background.png") type("image/png") + ); background-size: cover; background-position: center; background-repeat: no-repeat; } - .site-header { width: 100%; display: grid; @@ -45,7 +48,7 @@ background: rgba(255, 255, 255, 0.9); box-shadow: 0 6px 24px rgba(66, 66, 67, 0.767); grid-template-columns: 20% 1fr 20%; - grid-template-areas: 'left center right'; + grid-template-areas: "left center right"; } .header-left { @@ -81,7 +84,6 @@ letter-spacing: 0.1px; } - .main-content { width: 100%; max-width: var(--container-max); @@ -97,7 +99,6 @@ display: flex; } - .brand-name { font-size: 15px; } @@ -145,3 +146,65 @@ flex: 0 0 calc(100dvh - var(--site-header-height, 56px)); overflow: hidden; } + +.app-error { + display: grid; + min-height: min(62vh, 620px); + place-items: center; + padding: clamp(24px, 6vw, 72px) 20px; +} + +.app-error__card { + width: min(100%, 560px); + padding: clamp(24px, 5vw, 40px); + border: 1px solid var(--border-color, rgba(148, 163, 184, 0.35)); + border-radius: 20px; + background: var(--card-bg, rgba(15, 23, 42, 0.92)); + color: var(--text-primary, #f8fafc); + box-shadow: 0 24px 60px rgba(15, 23, 42, 0.2); + text-align: center; +} + +.app-error__card h1 { + margin: 0 0 12px; + font-size: clamp(1.6rem, 4vw, 2.2rem); +} + +.app-error__card p { + margin: 0; + color: var(--text-secondary, #cbd5e1); + line-height: 1.6; +} + +.app-error__actions { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 12px; + margin-top: 24px; +} + +.app-error__actions button, +.app-error__actions a { + display: inline-flex; + min-height: 42px; + align-items: center; + justify-content: center; + padding: 0 18px; + border: 1px solid currentColor; + border-radius: 999px; + font: inherit; + font-weight: 700; + text-decoration: none; + cursor: pointer; +} + +.app-error__actions button { + background: var(--brand-orange, #f59e0b); + color: #111827; +} + +.app-error__actions a { + background: transparent; + color: inherit; +} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 1b0cbf0..d9a5c6b 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -3,12 +3,62 @@ import { useEffect, useState } from "react"; import { useLocation } from "react-router-dom"; import "./App.css"; +import "./assets/styles/GlobalUiConsistency.css"; import { Routing } from "./Routing"; +import { AppErrorBoundary } from "./components/AppErrorBoundary"; import Footer from "./components/Footer"; import { NavBar } from "./components/NavBar"; +import { usePageTracking } from "./hooks/usePageTracking"; +import { useRouteMetadata } from "./hooks/useRouteMetadata"; type ThemeMode = "light" | "dark"; +const ERROR_TEXT_SELECTOR = [ + '[role="alert"]', + ".notification.error", + ".fabric-search__error", + ".grid-error", + ".upload-submit-error-below", + ".db-panel__notification--error", + ".description-wrapper .description-box.error", + ".analysis-popup__card", + ".app-error__card", +].join(","); + +const setErrorTextWhite = (errorBox: HTMLElement): void => { + const elements = [errorBox, ...errorBox.querySelectorAll("*")]; + + for (const element of elements) { + element.style.setProperty("color", "#ffffff", "important"); + element.style.setProperty( + "-webkit-text-fill-color", + "#ffffff", + "important", + ); + } +}; + +const applyErrorTextContrast = (root: ParentNode): void => { + const errorBoxes = new Set(); + + if (root instanceof HTMLElement) { + if (root.matches(ERROR_TEXT_SELECTOR)) { + errorBoxes.add(root); + } + + const containingError = root.closest(ERROR_TEXT_SELECTOR); + if (containingError) { + errorBoxes.add(containingError); + } + } + + root + .querySelectorAll(ERROR_TEXT_SELECTOR) + .forEach((errorBox) => errorBoxes.add(errorBox)); + + errorBoxes.forEach(setErrorTextWhite); +}; + const SunIcon = () => (
-
+
- +
- + + +
- {!isWorkspaceRoute &&
} + {!isChatRoute &&
}
); }; diff --git a/frontend/src/Routing.tsx b/frontend/src/Routing.tsx index e0bbfc1..ed2e6f8 100644 --- a/frontend/src/Routing.tsx +++ b/frontend/src/Routing.tsx @@ -1,22 +1,52 @@ -import { Suspense, lazy } from "react"; +import { Suspense, lazy, type ComponentType } from "react"; import Home from "./pages/Home"; -import { Route, Routes } from "react-router-dom"; -import { usePageTracking } from "./hooks/usePageTracking"; - -const ImageDescription = lazy(() => import("./pages/ImageDescriptor")); -const UploadPage = lazy(() => import("./pages/AudioForm")); -const ContentGrid = lazy(() => import("./pages/FabricList")); -const Search = lazy(() => import("./pages/FabricSearch")); -const Chat = lazy(() => import("./pages/FabricChat")); -const ComingSoon = lazy(() => import("./pages/ComingSoon")); -const ContactUs = lazy(() => +import { Navigate, Route, Routes } from "react-router-dom"; + +type RouteModule = { default: ComponentType }; +type RouteLoader = () => Promise; + +const ROUTE_IMPORT_RETRY_DELAY_MS = 250; + +/** + * Retry one transient route-module fetch before handing the error to the route + * boundary. This keeps a temporary dev-server or network interruption from + * immediately replacing the page with the fatal fallback. + */ +const loadRouteWithRetry = async (loader: RouteLoader): Promise => { + try { + return await loader(); + } catch (firstError) { + await new Promise((resolve) => { + setTimeout(resolve, ROUTE_IMPORT_RETRY_DELAY_MS); + }); + + try { + return await loader(); + } catch { + throw firstError; + } + } +}; + +const lazyRoute = (loader: RouteLoader) => + lazy(() => loadRouteWithRetry(loader)); + +const ImageDescription = lazyRoute(() => import("./pages/ImageDescriptor")); +const UploadPage = lazyRoute(() => import("./pages/AudioForm")); +const ContentGrid = lazyRoute(() => import("./pages/FabricList")); +const Search = lazyRoute(() => import("./pages/FabricSearch")); +const Chat = lazyRoute(() => import("./pages/FabricChat")); +const ComingSoon = lazyRoute(() => import("./pages/ComingSoon")); +const ContactUs = lazyRoute(() => import("./pages/Contact").then((module) => ({ default: module.ContactUs })), ); -const Reader = lazy(() => import("./pages/Reader")); -const CardReader = lazy(() => import("./pages/PanCardReader")); -const AadhaarCardReader = lazy(() => import("./pages/AadhaarCardReader")); -const NotFound = lazy(() => - import("./components/NotFound").then((module) => ({ default: module.NotFound })), +const Reader = lazyRoute(() => import("./pages/Reader")); +const CardReader = lazyRoute(() => import("./pages/PanCardReader")); +const AadhaarCardReader = lazyRoute(() => import("./pages/AadhaarCardReader")); +const NotFound = lazyRoute(() => + import("./components/NotFound").then((module) => ({ + default: module.NotFound, + })), ); const RouteFallback = () => ( @@ -26,7 +56,6 @@ const RouteFallback = () => ( ); export const Routing = () => { - usePageTracking(); return ( }> @@ -45,7 +74,11 @@ export const Routing = () => { } /> } /> } /> - } /> + } /> + } + /> ); diff --git a/frontend/src/assets/styles/AnalysisHeaderVisibilityParity.css b/frontend/src/assets/styles/AnalysisHeaderVisibilityParity.css new file mode 100644 index 0000000..e29ecb0 --- /dev/null +++ b/frontend/src/assets/styles/AnalysisHeaderVisibilityParity.css @@ -0,0 +1,163 @@ +/* + * Analysis header visibility parity. + * + * Loaded after the existing Analysis accent fixes so the Analysis route uses + * the exact shared navbar and theme-toggle treatment used by the other pages. + * This file is presentation-only and does not change routing or theme logic. + */ + +/* Guard against older Analysis-specific opacity/filter rules dimming header + content as a group. */ +.app-wrapper--analysis + .site-header + :is( + .header-left, + .header-center, + .header-right, + .mobile-nav-wrapper, + .header-nav, + .header-nav ul, + .brand-name, + .logo-mark + ) { + opacity: 1 !important; + filter: none !important; + mix-blend-mode: normal !important; + visibility: visible !important; +} + +/* Match the brand and inactive navigation labels used outside Analysis. */ +.app-wrapper--analysis .site-header .brand-name { + color: var(--tz-cotton) !important; + -webkit-text-fill-color: var(--tz-cotton) !important; + font-weight: 800 !important; + text-shadow: none !important; +} + +.app-wrapper--analysis + .site-header + #primary-navigation.header-nav + a:not(.active) { + color: var(--tz-muted) !important; + -webkit-text-fill-color: var(--tz-muted) !important; + font-weight: 650 !important; + opacity: 1 !important; + filter: none !important; + text-shadow: none !important; +} + +.app-wrapper--analysis + .site-header + #primary-navigation.header-nav + a:not(.active):is(:hover, :focus-visible) { + color: var(--tz-cotton) !important; + -webkit-text-fill-color: var(--tz-cotton) !important; +} + +/* Keep the active Analysis route identical to the active route on Upload and + the rest of the application. */ +:root[data-theme="light"] + .app-wrapper--analysis + .site-header + #primary-navigation.header-nav + a.active[aria-current="page"] { + background-color: transparent !important; + background-image: linear-gradient( + 135deg, + var(--tz-accent-control-surface, #8a4f08), + var(--tz-accent-control-surface-hover, #a45d0a) + ) !important; + border-color: var(--tz-accent-control-surface, #8a4f08) !important; + color: #ffffff !important; + -webkit-text-fill-color: #ffffff !important; + font-weight: 650 !important; + opacity: 1 !important; + filter: none !important; + text-shadow: none !important; +} + +:root[data-theme="dark"] + .app-wrapper--analysis + .site-header + #primary-navigation.header-nav + a.active[aria-current="page"] { + background-color: var(--tz-accent-control-surface, #e0a82e) !important; + background-image: none !important; + border-color: var(--tz-accent-control-surface, #e0a82e) !important; + color: #171101 !important; + -webkit-text-fill-color: #171101 !important; + font-weight: 650 !important; + opacity: 1 !important; + filter: none !important; + text-shadow: none !important; +} + +/* Reapply the shared theme-toggle surface and shadow at route-level + specificity. This prevents Analysis-only CSS from flattening its glow. */ +.app-wrapper--analysis .site-header #theme.theme-toggle { + background: linear-gradient( + 135deg, + var(--tz-button-ghost-bg), + var(--tz-panel-2) + ) !important; + border-color: var(--tz-line) !important; + color: var(--tz-cotton) !important; + box-shadow: var(--tz-shadow-soft) !important; + opacity: 1 !important; + filter: none !important; + mix-blend-mode: normal !important; + visibility: visible !important; +} + +.app-wrapper--analysis .site-header #theme.theme-toggle:hover { + background: linear-gradient( + 135deg, + var(--tz-button-ghost-hover-bg), + var(--tz-panel-3) + ) !important; + border-color: var(--tz-turmeric) !important; + color: var(--tz-cotton) !important; + box-shadow: var(--tz-shadow-soft) !important; +} + +.app-wrapper--analysis .site-header #theme.theme-toggle:focus-visible { + outline: 2px solid var(--tz-ui-accent, var(--tz-turmeric)) !important; + outline-offset: 3px !important; + box-shadow: + var(--tz-shadow-soft), + var(--tz-focus) !important; +} + +.app-wrapper--analysis + .site-header + #theme.theme-toggle + :is(.theme-toggle__track, .theme-toggle__icon, .theme-toggle__thumb) { + filter: none !important; + mix-blend-mode: normal !important; + visibility: visible !important; +} + +.app-wrapper--analysis + .site-header + #theme.theme-toggle + .theme-toggle__icon { + opacity: 0.54 !important; +} + +.app-wrapper--analysis + .site-header + #theme.theme-toggle + .theme-toggle__thumb { + opacity: 1 !important; + box-shadow: 0 6px 14px rgba(0, 0, 0, 0.2) !important; +} + +.app-wrapper--analysis + .site-header + #theme.theme-toggle + :is(.theme-toggle__icon, .theme-toggle__thumb) + svg { + color: currentColor !important; + opacity: 1 !important; + filter: none !important; +} diff --git a/frontend/src/assets/styles/AnalysisUploadButtonParity.css b/frontend/src/assets/styles/AnalysisUploadButtonParity.css new file mode 100644 index 0000000..c4984a1 --- /dev/null +++ b/frontend/src/assets/styles/AnalysisUploadButtonParity.css @@ -0,0 +1,582 @@ +/* + * Analysis route accent parity with the Upload route. + * + * This stylesheet is intentionally loaded after ReaderAnalysisContrastFixes.css + * so the Analysis navbar pill and primary controls use the same theme surfaces + * as Upload without changing any component behaviour or disabled state. + */ + +/* Active Analysis navbar item: match the active Upload navbar treatment. */ +:root[data-theme="light"] + .app-wrapper--analysis + .site-header + .header-nav + a.active { + background: linear-gradient( + 135deg, + var(--tz-accent-control-surface, #8a4f08), + var(--tz-accent-control-surface-hover, #a45d0a) + ) !important; + border-color: var(--tz-accent-control-surface, #8a4f08) !important; + color: #ffffff !important; + -webkit-text-fill-color: #ffffff !important; + text-shadow: none !important; + opacity: 1 !important; +} + +:root[data-theme="dark"] + .app-wrapper--analysis + .site-header + .header-nav + a.active { + background: var(--tz-accent-control-surface, #e0a82e) !important; + border-color: var(--tz-accent-control-surface, #e0a82e) !important; + color: #111827 !important; + -webkit-text-fill-color: #111827 !important; + text-shadow: none !important; + opacity: 1 !important; +} + +/* Short/Long and Prev/Next: match Upload's Start Recording surface exactly. */ +:root[data-theme="light"] + .app-wrapper--analysis + .analysis-page + :is( + .preview-buttons-container .analysis-btn, + .description-wrapper .nav-buttons .nav-btn + ) { + background: var(--tz-accent-control-surface, #8a4f08) !important; + border-color: var(--tz-accent-control-surface, #8a4f08) !important; + color: #ffffff !important; + -webkit-text-fill-color: #ffffff !important; + text-shadow: none !important; + filter: none !important; +} + +:root[data-theme="dark"] + .app-wrapper--analysis + .analysis-page + :is( + .preview-buttons-container .analysis-btn, + .description-wrapper .nav-buttons .nav-btn + ) { + background: var(--tz-accent-control-surface, #e0a82e) !important; + border-color: var(--tz-accent-control-surface, #e0a82e) !important; + color: #111827 !important; + -webkit-text-fill-color: #111827 !important; + text-shadow: none !important; + filter: none !important; +} + +/* Preserve disabled functionality while preventing legacy opacity/background + rules from changing the requested Upload-matched hue. The not-allowed cursor + and removed shadow continue to communicate that the control is unavailable. */ +:root[data-theme] + .app-wrapper--analysis + .analysis-page + :is( + .preview-buttons-container .analysis-btn, + .description-wrapper .nav-buttons .nav-btn + ):is(:disabled, .disabled) { + opacity: 1 !important; + cursor: not-allowed !important; + box-shadow: none !important; + transform: none !important; +} + +:root[data-theme="light"] + .app-wrapper--analysis + .analysis-page + :is( + .preview-buttons-container .analysis-btn, + .description-wrapper .nav-buttons .nav-btn + ):hover:not(:disabled):not(.disabled) { + background: var(--tz-accent-control-surface-hover, #a45d0a) !important; + border-color: var(--tz-accent-control-surface-hover, #a45d0a) !important; + color: #ffffff !important; + -webkit-text-fill-color: #ffffff !important; +} + +:root[data-theme="dark"] + .app-wrapper--analysis + .analysis-page + :is( + .preview-buttons-container .analysis-btn, + .description-wrapper .nav-buttons .nav-btn + ):hover:not(:disabled):not(.disabled) { + background: var(--tz-accent-control-surface-hover, #f0c463) !important; + border-color: var(--tz-accent-control-surface-hover, #f0c463) !important; + color: #111827 !important; + -webkit-text-fill-color: #111827 !important; +} + +.app-wrapper--analysis + .analysis-page + :is( + .preview-buttons-container .analysis-btn, + .description-wrapper .nav-buttons .nav-btn + ) + svg { + color: currentColor !important; + fill: currentColor !important; + stroke: currentColor !important; +} + +/* + * Final Analysis navbar parity guard. + * + * NavLink exposes aria-current="page" on the active route. Targeting that + * semantic state prevents older route-specific navbar rules from restoring + * the muted brown Analysis pill after the page-level styles are loaded. + * The values below intentionally mirror the active Upload route treatment. + */ +:root[data-theme="light"] + .app-wrapper--analysis + .site-header + #primary-navigation.header-nav + a.active[aria-current="page"] { + background-color: var(--tz-accent-control-surface, #8a4f08) !important; + background-image: linear-gradient( + 135deg, + var(--tz-accent-control-surface, #8a4f08), + var(--tz-accent-control-surface-hover, #a45d0a) + ) !important; + border-color: var(--tz-accent-control-surface, #8a4f08) !important; + color: #ffffff !important; + -webkit-text-fill-color: #ffffff !important; + filter: none !important; + mix-blend-mode: normal !important; + opacity: 1 !important; + text-shadow: none !important; +} + +:root[data-theme="dark"] + .app-wrapper--analysis + .site-header + #primary-navigation.header-nav + a.active[aria-current="page"] { + background-color: var(--tz-accent-control-surface, #e0a82e) !important; + background-image: none !important; + border-color: var(--tz-accent-control-surface, #e0a82e) !important; + color: #111827 !important; + -webkit-text-fill-color: #111827 !important; + filter: none !important; + mix-blend-mode: normal !important; + opacity: 1 !important; + text-shadow: none !important; +} + +:root[data-theme="light"] + .app-wrapper--analysis + .site-header + #primary-navigation.header-nav + a.active[aria-current="page"]:hover, +:root[data-theme="light"] + .app-wrapper--analysis + .site-header + #primary-navigation.header-nav + a.active[aria-current="page"]:focus-visible { + background-color: var(--tz-accent-control-surface-hover, #a45d0a) !important; + background-image: linear-gradient( + 135deg, + var(--tz-accent-control-surface-hover, #a45d0a), + var(--tz-accent-control-surface, #8a4f08) + ) !important; + border-color: var(--tz-accent-control-surface-hover, #a45d0a) !important; + color: #ffffff !important; + -webkit-text-fill-color: #ffffff !important; +} + +:root[data-theme="dark"] + .app-wrapper--analysis + .site-header + #primary-navigation.header-nav + a.active[aria-current="page"]:hover, +:root[data-theme="dark"] + .app-wrapper--analysis + .site-header + #primary-navigation.header-nav + a.active[aria-current="page"]:focus-visible { + background-color: var(--tz-accent-control-surface-hover, #f0c463) !important; + background-image: none !important; + border-color: var(--tz-accent-control-surface-hover, #f0c463) !important; + color: #111827 !important; + -webkit-text-fill-color: #111827 !important; +} + +/* + * Analysis header visibility parity (compatibility form). + * + * Keep this correction in the already-imported Analysis parity stylesheet so + * it applies cleanly even when a branch already contains the earlier standalone + * AnalysisHeaderVisibilityParity.css file or a different main.tsx import list. + */ +.app-wrapper--analysis + .site-header + :is( + .header-left, + .header-center, + .header-right, + .mobile-nav-wrapper, + .header-nav, + .header-nav ul, + .brand-name, + .logo-mark + ) { + opacity: 1 !important; + filter: none !important; + mix-blend-mode: normal !important; + visibility: visible !important; +} + +.app-wrapper--analysis .site-header .brand-name { + color: var(--tz-cotton) !important; + -webkit-text-fill-color: var(--tz-cotton) !important; + font-weight: 800 !important; + text-shadow: none !important; +} + +.app-wrapper--analysis + .site-header + #primary-navigation.header-nav + a:not(.active) { + color: var(--tz-muted) !important; + -webkit-text-fill-color: var(--tz-muted) !important; + font-weight: 650 !important; + opacity: 1 !important; + filter: none !important; + text-shadow: none !important; +} + +.app-wrapper--analysis + .site-header + #primary-navigation.header-nav + a:not(.active):is(:hover, :focus-visible) { + color: var(--tz-cotton) !important; + -webkit-text-fill-color: var(--tz-cotton) !important; +} + +.app-wrapper--analysis .site-header #theme.theme-toggle { + background: linear-gradient( + 135deg, + var(--tz-button-ghost-bg), + var(--tz-panel-2) + ) !important; + border-color: var(--tz-line) !important; + color: var(--tz-cotton) !important; + box-shadow: var(--tz-shadow-soft) !important; + opacity: 1 !important; + filter: none !important; + mix-blend-mode: normal !important; + visibility: visible !important; +} + +.app-wrapper--analysis .site-header #theme.theme-toggle:hover { + background: linear-gradient( + 135deg, + var(--tz-button-ghost-hover-bg), + var(--tz-panel-3) + ) !important; + border-color: var(--tz-turmeric) !important; + color: var(--tz-cotton) !important; + box-shadow: var(--tz-shadow-soft) !important; +} + +.app-wrapper--analysis .site-header #theme.theme-toggle:focus-visible { + outline: 2px solid var(--tz-ui-accent, var(--tz-turmeric)) !important; + outline-offset: 3px !important; + box-shadow: + var(--tz-shadow-soft), + var(--tz-focus) !important; +} + +.app-wrapper--analysis + .site-header + #theme.theme-toggle + :is(.theme-toggle__track, .theme-toggle__icon, .theme-toggle__thumb) { + filter: none !important; + mix-blend-mode: normal !important; + visibility: visible !important; +} + +.app-wrapper--analysis + .site-header + #theme.theme-toggle + .theme-toggle__icon { + opacity: 0.54 !important; +} + +.app-wrapper--analysis + .site-header + #theme.theme-toggle + .theme-toggle__thumb { + opacity: 1 !important; + box-shadow: 0 6px 14px rgba(0, 0, 0, 0.2) !important; +} + +.app-wrapper--analysis + .site-header + #theme.theme-toggle + :is(.theme-toggle__icon, .theme-toggle__thumb) + svg { + color: currentColor !important; + opacity: 1 !important; + filter: none !important; +} + +/* + * Final Analysis navbar/action colour parity. + * + * Keep the active Analysis NavLink and all four Analysis action controls on one + * shared surface declaration. Using one rule avoids small colour differences + * caused by separate gradients, shadows, inherited opacity, or route-specific + * filter rules. This is visual-only; disabled state and click behaviour remain + * owned by the existing components. + */ +:root[data-theme="light"] .app-wrapper--analysis { + --analysis-action-parity-surface: #8a4f08; + --analysis-action-parity-hover: #a45d0a; + --analysis-action-parity-border: #8a4f08; + --analysis-action-parity-foreground: #ffffff; + --analysis-action-parity-shadow: 0 10px 24px rgba(138, 79, 8, 0.24); +} + +:root[data-theme="dark"] .app-wrapper--analysis { + --analysis-action-parity-surface: #e0a82e; + --analysis-action-parity-hover: #f0c463; + --analysis-action-parity-border: #e0a82e; + --analysis-action-parity-foreground: #111827; + --analysis-action-parity-shadow: 0 10px 24px rgba(224, 168, 46, 0.24); +} + +/* Reset the desktop header ancestry so no route-level opacity/filter can mute + the active pill after its own colour has been resolved. Do not touch the + mobile menu
    transform/opacity that controls opening and closing. */ +.app-wrapper--analysis .site-header, +.app-wrapper--analysis .site-header .header-center, +.app-wrapper--analysis .site-header .mobile-nav-wrapper, +.app-wrapper--analysis .site-header #primary-navigation.header-nav, +.app-wrapper--analysis .site-header #primary-navigation.header-nav > ul > li { + opacity: 1 !important; + filter: none !important; + mix-blend-mode: normal !important; +} + +:root[data-theme] + .app-wrapper--analysis + :is( + .site-header + #primary-navigation.header-nav + a.active[aria-current="page"], + .analysis-page .preview-buttons-container .analysis-btn, + .analysis-page .description-wrapper .nav-buttons .nav-btn + ) { + background-color: var(--analysis-action-parity-surface) !important; + background-image: none !important; + background-blend-mode: normal !important; + border-color: var(--analysis-action-parity-border) !important; + color: var(--analysis-action-parity-foreground) !important; + -webkit-text-fill-color: var( + --analysis-action-parity-foreground + ) !important; + box-shadow: + inset 0 0 0 999px var(--analysis-action-parity-surface), + var(--analysis-action-parity-shadow) !important; + opacity: 1 !important; + filter: none !important; + mix-blend-mode: normal !important; + text-shadow: none !important; +} + +:root[data-theme] + .app-wrapper--analysis + .site-header + #primary-navigation.header-nav + a.active[aria-current="page"] { + position: relative !important; + isolation: isolate !important; + overflow: hidden !important; + font-weight: 800 !important; +} + +:root[data-theme] + .app-wrapper--analysis + :is( + .site-header + #primary-navigation.header-nav + a.active[aria-current="page"], + .analysis-page .preview-buttons-container .analysis-btn, + .analysis-page .description-wrapper .nav-buttons .nav-btn + ):hover:not(:disabled):not(.disabled), +:root[data-theme] + .app-wrapper--analysis + .site-header + #primary-navigation.header-nav + a.active[aria-current="page"]:focus-visible { + background-color: var(--analysis-action-parity-hover) !important; + background-image: none !important; + border-color: var(--analysis-action-parity-hover) !important; + color: var(--analysis-action-parity-foreground) !important; + -webkit-text-fill-color: var( + --analysis-action-parity-foreground + ) !important; + box-shadow: + inset 0 0 0 999px var(--analysis-action-parity-hover), + var(--analysis-action-parity-shadow) !important; +} + +/* + * Workspace zoom-out stability. + * + * Browser zoom-out increases the effective CSS viewport. The legacy Analysis + * drawer rule pinned the result grid to a fixed left margin, while the empty + * Chat body was allowed to flex across the entire enlarged viewport height. + * Keep both workspaces bounded and centred without changing component logic. + */ + +/* Analysis: centre the complete workspace inside the area that remains beside + the fixed sample drawer. The shared variable also keeps the reserved space + and the actual drawer width in lock-step. */ +.analysis-page { + --analysis-drawer-inline-size: clamp(248px, 19vw, 272px); + --analysis-drawer-gutter: clamp(18px, 2vw, 32px); +} + +.analysis-page .drawer-panel { + width: var(--analysis-drawer-inline-size); +} + +@media (min-width: 981px) { + .home-container.analysis-page.drawer-open { + padding-inline-start: var(--analysis-drawer-gutter); + padding-inline-end: calc( + var(--analysis-drawer-inline-size) + var(--analysis-drawer-gutter) + ); + } + + .analysis-page.drawer-open .result-wrapper.analysis-grid, + .analysis-page.drawer-open .result-wrapper.grid { + width: min(1120px, 100%) !important; + margin-inline: auto !important; + } +} + +/* Chat: the landing state is one coherent shell. Cap its vertical growth and + centre the top bar + body as a unit so zoom-out cannot send the header to the + top, prompts to the middle, and composer to the bottom of the viewport. */ +.app-wrapper--chat .page-root.is-empty { + justify-content: center; + align-items: center; + gap: 0; + padding-block: clamp(10px, 2vh, 24px); +} + +.app-wrapper--chat .page-root.is-empty .chat-card-topbar, +.app-wrapper--chat .page-root.is-empty .chat-body { + width: min(1048px, calc(100% - 32px)); + margin-inline: auto; +} + +.app-wrapper--chat .page-root.is-empty .chat-card-topbar { + margin-top: 0; + margin-bottom: 12px; +} + +.app-wrapper--chat .page-root.is-empty .chat-body { + flex: 0 1 auto; + height: min(700px, calc(100dvh - var(--site-header-height, 56px) - 98px)); + min-height: 0; + max-height: 700px; + margin-top: 0; +} + +.app-wrapper--chat .page-root.is-empty .chat-scroll-area { + flex: 1 1 auto; + min-height: 0; + justify-content: center; +} + +@media (max-width: 720px) { + .app-wrapper--chat .page-root.is-empty { + justify-content: flex-start; + padding-block: 10px; + } + + .app-wrapper--chat .page-root.is-empty .chat-card-topbar, + .app-wrapper--chat .page-root.is-empty .chat-body { + width: calc(100% - 20px); + } + + .app-wrapper--chat .page-root.is-empty .chat-body { + height: calc(100dvh - var(--site-header-height, 56px) - 92px); + max-height: none; + } +} + + +/* + * Analysis header stacking and native navbar parity. + * + * The Analysis workspace contains fixed layers with high z-index values. Keep + * the shared site header above those layers, then let its selected route use + * the same theme surfaces as every other navbar item. This replaces the + * runtime computed-style copy, which could not correct a layer painted above + * the header and made the result timing-dependent on lazy route mounting. + */ +.app-wrapper--analysis .site-header { + position: relative; + z-index: 1100; + isolation: isolate; +} + +:root[data-theme="light"] + .app-wrapper--analysis + .site-header + #primary-navigation.header-nav + a.active[aria-current="page"] { + background: linear-gradient( + 135deg, + var(--tz-accent-control-surface, #8a4f08), + var(--tz-accent-control-surface-hover, #a45d0a) + ) !important; + border-color: var(--tz-accent-control-surface, #8a4f08) !important; + color: #ffffff !important; + -webkit-text-fill-color: #ffffff !important; + box-shadow: none !important; + opacity: 1 !important; + filter: none !important; + mix-blend-mode: normal !important; +} + +:root[data-theme="dark"] + .app-wrapper--analysis + .site-header + #primary-navigation.header-nav + a.active[aria-current="page"] { + background: var(--tz-turmeric, #e0a82e) !important; + border-color: var(--tz-turmeric, #e0a82e) !important; + color: #171101 !important; + -webkit-text-fill-color: #171101 !important; + box-shadow: none !important; + opacity: 1 !important; + filter: none !important; + mix-blend-mode: normal !important; +} + +:root[data-theme="light"] + .app-wrapper--analysis + .site-header + #primary-navigation.header-nav + a.active[aria-current="page"]:is(:hover, :focus-visible) { + background: var(--tz-accent-control-surface-hover, #a45d0a) !important; + border-color: var(--tz-accent-control-surface-hover, #a45d0a) !important; +} + +:root[data-theme="dark"] + .app-wrapper--analysis + .site-header + #primary-navigation.header-nav + a.active[aria-current="page"]:is(:hover, :focus-visible) { + background: var(--tz-turmeric-soft, #f0c463) !important; + border-color: var(--tz-turmeric-soft, #f0c463) !important; +} diff --git a/frontend/src/assets/styles/ChatRouteLayoutStability.css b/frontend/src/assets/styles/ChatRouteLayoutStability.css new file mode 100644 index 0000000..3549c29 --- /dev/null +++ b/frontend/src/assets/styles/ChatRouteLayoutStability.css @@ -0,0 +1,159 @@ +/* + * Chat route layout stability. + * + * The Chat page is lazy-loaded. In development, route CSS can be inserted in a + * different order after navigating away and back, which previously allowed the + * empty-state centring rules to replace the normal full-height chat layout. + * These route-scoped final declarations keep the same structure regardless of + * stylesheet insertion order or browser zoom level. + */ +.app-wrapper--chat .main-content { + min-width: 0; +} + +.app-wrapper--chat .page-root { + --chat-route-workspace-width: min(1048px, calc(100vw - 32px)); + + display: grid !important; + grid-template-rows: auto minmax(0, 1fr) !important; + justify-content: stretch !important; + align-content: stretch !important; + justify-items: stretch !important; + align-items: stretch !important; + gap: 0 !important; + width: 100% !important; + height: 100% !important; + min-height: 0 !important; + max-height: none !important; + padding: clamp(10px, 2vh, 18px) 16px + max(12px, env(safe-area-inset-bottom)) !important; + overflow: hidden !important; +} + +.app-wrapper--chat .page-root .chat-card-topbar, +.app-wrapper--chat .page-root .chat-body { + width: var(--chat-route-workspace-width) !important; + max-width: none !important; + margin-inline: auto !important; +} + +.app-wrapper--chat .page-root .chat-card-topbar { + align-self: start !important; + margin-top: 0 !important; + margin-bottom: 12px !important; +} + +.app-wrapper--chat .page-root .chat-body, +.app-wrapper--chat .page-root.is-empty .chat-body { + display: flex !important; + flex-direction: column !important; + align-self: stretch !important; + height: auto !important; + min-height: 0 !important; + max-height: none !important; + margin-top: 0 !important; + margin-bottom: 0 !important; + overflow: hidden !important; +} + +.app-wrapper--chat .page-root .chat-scroll-area, +.app-wrapper--chat .page-root.is-empty .chat-scroll-area { + display: flex !important; + flex: 1 1 auto !important; + flex-direction: column !important; + justify-content: flex-start !important; + align-items: stretch !important; + min-height: 0 !important; + padding: clamp(20px, 5vh, 56px) 8px 20px !important; + overflow-x: hidden !important; + overflow-y: auto !important; +} + +.app-wrapper--chat .page-root .empty-state-large { + flex: 0 0 auto !important; + margin-inline: auto !important; +} + +/* Keep the landing prompt close to the assistant toolbar. The previous 5vh + top padding became a large empty band at wide viewports and browser zoom-out. */ +.app-wrapper--chat .page-root.is-empty .chat-scroll-area { + padding-top: clamp(8px, 1.4vh, 18px) !important; +} + +.app-wrapper--chat .page-root .composer { + flex: 0 0 auto !important; + width: 100% !important; + margin: auto 0 0 !important; +} + +/* + * A zoomed-out browser exposes a much wider CSS viewport. Grow the bounded + * workspace proportionally so the interface keeps the same readable physical + * size instead of collapsing into a small card in the middle of the screen. + */ +@media (min-width: 1800px) { + .app-wrapper--chat .page-root { + --chat-route-workspace-width: clamp(1048px, 42vw, 2400px); + } +} + +@media (max-width: 720px) { + .app-wrapper--chat .page-root { + --chat-route-workspace-width: calc(100vw - 20px); + + padding: 10px 0 max(10px, env(safe-area-inset-bottom)) !important; + } + + .app-wrapper--chat .page-root .chat-card-topbar { + margin-bottom: 8px !important; + } + + .app-wrapper--chat .page-root .chat-scroll-area, + .app-wrapper--chat .page-root.is-empty .chat-scroll-area { + padding: 16px 4px !important; + } +} + +/* Zoom-out width parity. + * The route workspace grows at wide effective viewports, but the landing copy + * and prompt grid previously stayed capped at 980px. Keep every visible Chat + * surface on the same width contract only when zoom-out exposes a wide CSS + * viewport; normal desktop, tablet and mobile geometry remains unchanged. */ +@media (min-width: 2600px) { + .app-wrapper--chat .page-root .empty-state-large { + width: 100% !important; + max-width: none !important; + padding-inline: 0 !important; + } + + .app-wrapper--chat .page-root .empty-state-large h3, + .app-wrapper--chat .page-root .empty-state-large p { + width: 100% !important; + max-width: none !important; + } + + .app-wrapper--chat .page-root .empty-state-large .chips { + width: 100% !important; + max-width: none !important; + padding-inline: 0 !important; + } + + .app-wrapper--chat .page-root .empty-state-large .chip { + min-height: clamp(44px, 1.7vw, 60px); + padding-inline: clamp(14px, 1vw, 28px); + font-size: clamp(14px, 0.58vw, 18px); + } + + .app-wrapper--chat .page-root .empty-state-large h3 { + font-size: clamp(24px, 1.05vw, 34px); + } + + .app-wrapper--chat .page-root .empty-state-large p { + font-size: clamp(15px, 0.62vw, 19px); + } + + .app-wrapper--chat .page-root .error[role="alert"] { + width: 100%; + max-width: none; + } +} diff --git a/frontend/src/assets/styles/Composer.css b/frontend/src/assets/styles/Composer.css index bb80954..25fb337 100644 --- a/frontend/src/assets/styles/Composer.css +++ b/frontend/src/assets/styles/Composer.css @@ -1014,3 +1014,510 @@ max-width: 100% ; } } + +/* Senior QA patch: Composer readability guard after global theme changes. */ +.composer .composer-input { + color: var(--tz-ui-text, #0f172a); + background: var(--tz-ui-surface, #ffffff); + border-color: var(--tz-ui-border, rgba(15, 23, 42, 0.14)); + opacity: 1; +} + +.composer .composer-input::placeholder { + color: var(--tz-ui-text-muted, #64748b); + opacity: 1; +} + +.composer .attach-btn.inside, +.composer .record-btn { + color: var(--tz-ui-text, #0f172a); + background: var(--tz-ui-control-bg, #ffffff); + border-color: var(--tz-ui-border, rgba(15, 23, 42, 0.14)); +} + +:root[data-theme="dark"] .composer .composer-input { + color: #f8fafc; + background: rgba(10, 15, 29, 0.92); + border-color: rgba(226, 232, 240, 0.24); +} + +:root[data-theme="dark"] .composer .composer-input::placeholder { + color: #cbd5e1; +} + +:root[data-theme="dark"] .composer .attach-btn.inside, +:root[data-theme="dark"] .composer .record-btn { + color: #f8fafc; + background: rgba(30, 41, 59, 0.96); + border-color: rgba(226, 232, 240, 0.24); +} + +/* Senior QA: Composer readability guard after global theme changes. */ +.composer .composer-input { + color: var(--tz-ui-text, #0f172a); + background: var(--tz-ui-surface, #ffffff); + border-color: var(--tz-ui-border, rgba(15, 23, 42, 0.14)); + opacity: 1; +} + +.composer .composer-input::placeholder { + color: var(--tz-ui-text-muted, #64748b); + opacity: 1; +} + +.composer .attach-btn.inside, +.composer .record-btn { + color: var(--tz-ui-text, #0f172a); + background: var(--tz-ui-control-bg, #ffffff); + border-color: var(--tz-ui-border, rgba(15, 23, 42, 0.14)); +} + +:root[data-theme="dark"] .composer .composer-input { + color: #f8fafc; + background: rgba(10, 15, 29, 0.92); + border-color: rgba(226, 232, 240, 0.24); +} + +:root[data-theme="dark"] .composer .composer-input::placeholder { + color: #cbd5e1; +} + +:root[data-theme="dark"] .composer .attach-btn.inside, +:root[data-theme="dark"] .composer .record-btn { + color: #f8fafc; + background: rgba(30, 41, 59, 0.96); + border-color: rgba(226, 232, 240, 0.24); +} + +/* QA follow-up: make upload-confirm modal titles readable after theme switching. */ +.composer-modal--confirm-upload { + width: min(520px, calc(100vw - 32px)); + padding: 18px; + border-radius: 16px; + background: var(--tz-ui-surface-raised, #ffffff); + color: var(--tz-ui-text, #0f172a); +} + +.composer-modal__layout { + display: flex; + gap: 14px; + align-items: center; +} + +.composer-modal__layout--audio { + align-items: flex-start; +} + +.composer-modal__media { + flex: 0 0 128px; + min-width: 0; +} + +.composer-modal__content { + flex: 1 1 auto; + min-width: 0; +} + +.composer-modal__title { + margin-bottom: 7px; + color: var(--tz-ui-text, #0f172a); + font-size: 16px; + font-weight: 900; + line-height: 1.25; +} + +.composer-modal__meta, +.composer-modal__hint { + color: var(--tz-ui-text-muted, #475569); + font-size: 14px; + line-height: 1.35; + overflow-wrap: anywhere; +} + +.composer-modal__hint { + margin-top: 8px; + font-size: 13px; +} + +.composer-modal__actions { + margin-top: 14px; + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +.composer-modal__button { + min-height: 36px; + padding: 8px 13px; + border-radius: 9px; + font-weight: 800; + cursor: pointer; +} + +.composer-modal__button--confirm { + background: var(--tz-turmeric, #e0a82e); + border: 1px solid var(--tz-turmeric, #e0a82e); + color: #171101; +} + +.composer-modal__button--cancel { + background: var(--tz-ui-control-bg, #ffffff); + border: 1px solid var(--tz-ui-border, rgba(15, 23, 42, 0.14)); + color: var(--tz-ui-text, #0f172a); +} + +.composer-modal__thumb { + width: 128px; + height: 128px; + border-radius: 10px; + object-fit: contain; +} + +.composer-modal__media audio { + width: 128px; + max-width: 100%; +} + +:root[data-theme="dark"] .composer-modal--confirm-upload { + background: rgba(15, 23, 42, 0.98); + color: #f8fafc; + border-color: rgba(226, 232, 240, 0.24); +} + +:root[data-theme="dark"] .composer-modal__title { + color: #f8fafc; +} + +:root[data-theme="dark"] .composer-modal__meta, +:root[data-theme="dark"] .composer-modal__hint { + color: #cbd5e1; +} + +:root[data-theme="dark"] .composer-modal__button--cancel { + background: rgba(30, 41, 59, 0.96); + border-color: rgba(226, 232, 240, 0.24); + color: #f8fafc; +} + +:root[data-theme="light"] .stop-overlay-btn { + background: #dc2626; + border: 1px solid #b91c1c; + color: #ffffff; + box-shadow: 0 10px 24px rgba(220, 38, 38, 0.22); +} + +:root[data-theme="light"] .stop-overlay-inner { + background: rgba(255, 255, 255, 0.86); + border: 1px solid rgba(220, 38, 38, 0.22); +} + +@media (max-width: 560px) { + .composer-modal__layout { + flex-direction: column; + align-items: stretch; + } + + .composer-modal__media { + flex-basis: auto; + } +} + +/* Senior QA final: composer, stop control, and upload-confirm modals stay readable in both themes. */ +.page-root .composer { + padding: 10px 10px max(16px, env(safe-area-inset-bottom)); + border-radius: 18px; +} + +.page-root .composer-row { + align-items: center; +} + +.page-root .composer-input-wrapper { + border-radius: 16px; + background: var(--tz-ui-control-bg, rgba(255, 255, 255, 0.96)); + border: 1px solid var(--tz-ui-border, rgba(15, 23, 42, 0.14)); +} + +.page-root .composer-input { + color: var(--tz-ui-text, #0f172a); + background: transparent; + border: 0; + min-height: 50px; +} + +.page-root .composer-input::placeholder { + color: var(--tz-ui-text-faint, #64748b); + opacity: 1; +} + +:root[data-theme="dark"] .page-root .composer-input-wrapper { + background: rgba(15, 23, 42, 0.88); + border-color: rgba(226, 232, 240, 0.20); +} + +:root[data-theme="dark"] .page-root .composer-input { + color: #f8fafc; +} + +.page-root .attach-btn.inside, +.page-root .send-btn.inside { + top: 50%; + transform: translateY(-50%); +} + +.page-root .attach-btn.inside { + color: var(--tz-ui-text-muted, #475569); + background: var(--tz-ui-surface-subtle, #f1f5f9); + border: 1px solid var(--tz-ui-border, rgba(15, 23, 42, 0.14)); +} + +:root[data-theme="dark"] .page-root .attach-btn.inside { + color: #e2e8f0; + background: rgba(30, 41, 59, 0.92); + border-color: rgba(226, 232, 240, 0.20); +} + +.page-root .composer-audio-row { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 10px; + padding-top: 8px; +} + +.page-root .record-btn { + min-height: 38px; + border-radius: 999px; + padding: 0 14px; + font-weight: 800; + color: var(--tz-ui-text, #0f172a); + background: var(--tz-ui-control-bg, #ffffff); + border: 1px solid var(--tz-ui-border, rgba(15, 23, 42, 0.16)); +} + +:root[data-theme="dark"] .page-root .record-btn { + color: #f8fafc; + background: rgba(30, 41, 59, 0.92); + border-color: rgba(226, 232, 240, 0.20); +} + +.page-root .stop-overlay-btn { + min-height: var(--tz-control-height, 44px); + padding: 0 18px; + border-radius: 999px; + border: 1px solid rgba(185, 28, 28, 0.42); + background: linear-gradient(135deg, var(--tz-danger, #dc2626), var(--tz-danger-hover, #b91c1c)); + color: var(--tz-danger-text, #ffffff); + font-weight: 900; + box-shadow: 0 12px 28px rgba(220, 38, 38, 0.26); +} + +:root[data-theme="light"] .page-root .stop-overlay-btn { + background: linear-gradient(135deg, #ef4444, #b91c1c); + color: #ffffff; + border-color: rgba(127, 29, 29, 0.46); +} + +.composer-modal-overlay .composer-modal--confirm-upload { + background: var(--tz-ui-surface-raised, #ffffff); + color: var(--tz-ui-text, #0f172a); + border: 1px solid var(--tz-ui-border-strong, rgba(15, 23, 42, 0.24)); + box-shadow: var(--tz-ui-shadow-panel, 0 18px 48px rgba(15, 23, 42, 0.22)); +} + +.composer-modal-overlay .composer-modal__title { + color: var(--tz-ui-text, #0f172a); + font-weight: 900; +} + +.composer-modal-overlay .composer-modal__meta, +.composer-modal-overlay .composer-modal__hint { + color: var(--tz-ui-text-muted, #475569); + opacity: 1; +} + +:root[data-theme="dark"] .composer-modal-overlay .composer-modal--confirm-upload { + background: rgba(15, 23, 42, 0.96); + color: #f8fafc; + border-color: rgba(226, 232, 240, 0.24); +} + +:root[data-theme="dark"] .composer-modal-overlay .composer-modal__title { + color: #f8fafc; +} + +:root[data-theme="dark"] .composer-modal-overlay .composer-modal__meta, +:root[data-theme="dark"] .composer-modal-overlay .composer-modal__hint { + color: #cbd5e1; +} + +@media (max-height: 760px) { + .page-root .composer { + padding-top: 8px; + padding-bottom: max(12px, env(safe-area-inset-bottom)); + } + + .page-root .composer-input { + min-height: 46px; + } +} + +/* Senior QA top-up: upload confirmation audio preview must never overlap title/meta. */ +.composer-modal--audio-confirm { + width: min(560px, calc(100vw - 32px)); +} + +.composer-modal--audio-confirm .composer-modal__layout--audio { + display: block; +} + +.composer-modal--audio-confirm .composer-modal__content--audio { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + gap: 10px; +} + +.composer-modal--audio-confirm .composer-modal__title { + margin: 0; + width: 100%; + color: var(--tz-ui-text, #0f172a); + font-size: 17px; + line-height: 1.25; +} + +.composer-modal--audio-confirm .composer-modal__meta { + width: min(420px, 100%); + color: var(--tz-ui-text-muted, #475569); + overflow-wrap: anywhere; +} + +.composer-modal__audio-player { + width: 100%; + display: flex; + justify-content: center; + margin: 2px 0 4px; +} + +.composer-modal__audio-player audio { + width: min(360px, 100%); + min-width: 0; + max-width: 100%; + display: block; +} + +.composer-modal--audio-confirm .composer-modal__actions { + justify-content: center; +} + +:root[data-theme="dark"] .composer-modal--audio-confirm .composer-modal__title { + color: #f8fafc; +} + +:root[data-theme="dark"] .composer-modal--audio-confirm .composer-modal__meta, +:root[data-theme="dark"] .composer-modal--audio-confirm .composer-modal__hint { + color: #cbd5e1; +} + +@media (max-width: 520px) { + .composer-modal--confirm-upload { + padding: 16px; + } + + .composer-modal__layout--image { + display: grid; + grid-template-columns: 1fr; + text-align: center; + } + + .composer-modal__layout--image .composer-modal__media { + justify-self: center; + } + + .composer-modal__layout--image .composer-modal__actions { + justify-content: center; + } +} + + +/* Strict senior QA follow-up: upload confirm modals should read cleanly, not like a noisy blurred glitch. */ +.composer-modal-overlay { + background: rgba(8, 11, 20, 0.54); + backdrop-filter: blur(3px); + -webkit-backdrop-filter: blur(3px); +} + +:root[data-theme="light"] .composer-modal-overlay { + background: rgba(15, 23, 42, 0.34); +} + +.composer-modal--confirm-upload { + border-radius: 20px; + padding: 20px; +} + +.composer-modal--audio-confirm .composer-modal__content--audio { + gap: 12px; +} + +.composer-modal--audio-confirm .composer-modal__audio-player { + order: 2; + margin: 4px 0; +} + +.composer-modal--audio-confirm .composer-modal__title { + order: 1; + text-align: center; +} + +.composer-modal--audio-confirm .composer-modal__meta { + order: 3; + max-width: 440px; + text-align: center; +} + +.composer-modal--audio-confirm .composer-modal__hint { + order: 4; + text-align: center; +} + +.composer-modal--audio-confirm .composer-modal__actions { + order: 5; + margin-top: 4px; +} + +.page-root .composer-audio-row { + padding-bottom: max(8px, env(safe-area-inset-bottom)); +} + +.page-root .error, +.page-root .composer-error, +.page-root .chat-error { + max-width: min(900px, calc(100vw - 48px)); + margin-inline: auto; + border-radius: 14px; +} + + +/* v42: keep the in-composer cancellation action compact without changing its + click behavior or the surrounding composer layout. */ +.page-root .stop-overlay-btn { + min-height: 36px; + height: 36px; + padding: 0 14px; + gap: 5px; + font-size: 12px; + line-height: 1; + box-shadow: 0 8px 18px rgba(220, 38, 38, 0.22); + white-space: nowrap; +} + +@media (max-width: 480px) { + .page-root .stop-overlay-btn { + min-height: 34px; + height: 34px; + padding-inline: 12px; + font-size: 11px; + } +} diff --git a/frontend/src/assets/styles/ContentGrid.css b/frontend/src/assets/styles/ContentGrid.css index 28a4dc5..a4f94b1 100644 --- a/frontend/src/assets/styles/ContentGrid.css +++ b/frontend/src/assets/styles/ContentGrid.css @@ -149,6 +149,14 @@ figcaption { color: #ff0000; } +/* Error text must remain white on the shared red failure surface. */ +.grid-page .grid-error, +.grid-page .grid-error * { + color: #ffffff !important; + -webkit-text-fill-color: #ffffff !important; + text-shadow: none !important; +} + .lb-backdrop { position: fixed; inset: 0; @@ -385,3 +393,560 @@ figcaption { bottom: 58px; } } + +/* Senior QA patch: Fabric List contrast fixes without !important. + Keep pagination, totals, card meta, and audio labels readable in both themes. */ +.grid-page .grid-header, +.grid-page .grid-controls.inline, +.grid-page .media-card, +.grid-page .audio-box { + color: var(--tz-ui-text, var(--tz-cotton, #f8fafc)); +} + +.grid-page .grid-title-text, +.grid-page .grid-count-inline, +.grid-page .grid-controls.inline .grid-page { + color: var(--tz-ui-text, #f8fafc); + opacity: 1; + text-shadow: none; +} + +.grid-page .grid-count-inline { + font-weight: 850; +} + +.grid-page .grid-controls.inline .grid-page { + display: inline-flex; + align-items: center; + min-height: 34px; + padding: 0 12px; + border: 1px solid var(--tz-ui-border, rgba(226, 232, 240, 0.24)); + border-radius: 8px; + background: var(--tz-ui-surface-raised, rgba(30, 41, 59, 0.96)); + font-size: 14px; + font-weight: 800; +} + +.grid-page .grid-controls.inline button { + border-color: var(--tz-ui-border, rgba(226, 232, 240, 0.24)); + background: var(--tz-ui-control-bg, rgba(30, 41, 59, 0.94)); + color: var(--tz-ui-text, #f8fafc); +} + +.grid-page .grid-controls.inline button:hover:not(:disabled) { + background: var(--tz-ui-control-hover-bg, rgba(51, 65, 85, 0.98)); +} + +.grid-page .audio-box { + background: var(--tz-ui-surface-raised, rgba(30, 41, 59, 0.96)); + border: 1px solid var(--tz-ui-border, rgba(226, 232, 240, 0.24)); +} + +.grid-page .audio-label { + color: var(--tz-ui-text, #f8fafc); + opacity: 1; + text-shadow: none; +} + +.grid-page .media-meta, +.grid-page .media-meta time, +.grid-page .grid-page-subtitle { + color: var(--tz-ui-text-muted, #cbd5e1); + opacity: 1; +} + +:root[data-theme="light"] .grid-page .grid-title-text, +:root[data-theme="light"] .grid-page .grid-count-inline, +:root[data-theme="light"] .grid-page .grid-controls.inline .grid-page, +:root[data-theme="light"] .grid-page .audio-label { + color: #0f172a; +} + +:root[data-theme="light"] .grid-page .audio-box, +:root[data-theme="light"] .grid-page .grid-controls.inline .grid-page { + background: #ffffff; + border-color: rgba(15, 23, 42, 0.14); +} + +:root[data-theme="light"] .grid-page .media-meta, +:root[data-theme="light"] .grid-page .media-meta time, +:root[data-theme="light"] .grid-page .grid-page-subtitle { + color: #475569; +} + +/* Senior QA: Fabric List contrast fixes without forced-priority rules. + Keep pagination, totals, card meta, and audio labels readable in both themes. */ +.grid-page .grid-header, +.grid-page .grid-controls.inline, +.grid-page .media-card, +.grid-page .audio-box { + color: var(--tz-ui-text, var(--tz-cotton, #f8fafc)); +} + +.grid-page .grid-title-text, +.grid-page .grid-count-inline, +.grid-page .grid-controls.inline .grid-page-indicator { + color: var(--tz-ui-text, #f8fafc); + opacity: 1; + text-shadow: none; +} + +.grid-page .grid-count-inline { + font-weight: 850; +} + +.grid-page .grid-controls.inline .grid-page-indicator { + display: inline-flex; + align-items: center; + min-height: 34px; + padding: 0 12px; + border: 1px solid var(--tz-ui-border, rgba(226, 232, 240, 0.24)); + border-radius: 8px; + background: var(--tz-ui-surface-raised, rgba(30, 41, 59, 0.96)); + font-size: 14px; + font-weight: 800; +} + +.grid-page .grid-controls.inline button { + border-color: var(--tz-ui-border, rgba(226, 232, 240, 0.24)); + background: var(--tz-ui-control-bg, rgba(30, 41, 59, 0.94)); + color: var(--tz-ui-text, #f8fafc); +} + +.grid-page .grid-controls.inline button:hover:not(:disabled) { + background: var(--tz-ui-control-hover-bg, rgba(51, 65, 85, 0.98)); +} + +.grid-page .audio-box { + background: var(--tz-ui-surface-raised, rgba(30, 41, 59, 0.96)); + border: 1px solid var(--tz-ui-border, rgba(226, 232, 240, 0.24)); +} + +.grid-page .audio-label { + color: var(--tz-ui-text, #f8fafc); + opacity: 1; + text-shadow: none; +} + +.grid-page .media-meta, +.grid-page .media-meta time, +.grid-page .grid-page-subtitle { + color: var(--tz-ui-text-muted, #cbd5e1); + opacity: 1; +} + +:root[data-theme="light"] .grid-page .grid-title-text, +:root[data-theme="light"] .grid-page .grid-count-inline, +:root[data-theme="light"] .grid-page .grid-controls.inline .grid-page-indicator, +:root[data-theme="light"] .grid-page .audio-label { + color: #0f172a; +} + +:root[data-theme="light"] .grid-page .audio-box, +:root[data-theme="light"] .grid-page .grid-controls.inline .grid-page-indicator { + background: #ffffff; + border-color: rgba(15, 23, 42, 0.14); +} + +:root[data-theme="light"] .grid-page .media-meta, +:root[data-theme="light"] .grid-page .media-meta time, +:root[data-theme="light"] .grid-page .grid-page-subtitle { + color: #475569; +} + +/* Senior visual-system pass: list page readable metadata, intentional disabled state, consistent card rhythm. */ +.grid-page { + color: var(--tz-ui-text, var(--tz-cotton, #f8fafc)); + min-height: calc(100dvh - 68px); + padding-block: clamp(18px, 3vh, 32px) 48px; +} + +.grid-page .grid-header { + align-items: center; + margin: 24px 0 18px; +} + +.grid-page .grid-title-text, +.grid-page .grid-count-inline, +.grid-page .grid-controls.inline .grid-page-indicator { + color: var(--tz-ui-text, #f8fafc); +} + +.grid-page .grid-controls.inline button:disabled { + opacity: 1; + color: var(--tz-ui-text-faint, #94a3b8); + background: var(--tz-ui-surface-subtle, rgba(30, 41, 59, 0.58)); + border-color: var(--tz-ui-border, rgba(226, 232, 240, 0.2)); + cursor: not-allowed; +} + +.grid-page .media-grid { + align-items: stretch; +} + +.grid-page .media-card { + background: var(--tz-ui-surface-raised, rgba(23, 31, 51, 0.96)); + border-color: var(--tz-ui-border, rgba(226, 232, 240, 0.22)); + box-shadow: var(--tz-ui-shadow-panel, 0 18px 48px rgba(0, 0, 0, 0.22)); +} + +.grid-page .media-thumb img { + height: 250px; + object-fit: cover; +} + +.grid-page .media-name { + min-height: 34px; + padding: 8px 12px; + color: var(--tz-ui-text, #f8fafc); + background: rgba(15, 23, 42, 0.72); + font-style: normal; + font-weight: 800; + line-height: 1.2; +} + +.grid-page .media-meta { + border-top-color: var(--tz-ui-border, rgba(226, 232, 240, 0.22)); + color: var(--tz-ui-text-muted, #cbd5e1); +} + +.grid-page .audio-label { + color: var(--tz-ui-text, #f8fafc); + font-weight: 800; + letter-spacing: 0.01em; +} + +:root[data-theme="light"] .grid-page .media-card, +:root[data-theme="light"] .grid-page .audio-box { + background: var(--tz-ui-surface-raised, #ffffff); + border-color: var(--tz-ui-border, rgba(15, 23, 42, 0.14)); +} + +:root[data-theme="light"] .grid-page .media-name { + color: #0f172a; + background: rgba(255, 255, 255, 0.92); +} + +:root[data-theme="light"] .grid-page .grid-controls.inline button:disabled { + color: #64748b; + background: #f1f5f9; + border-color: rgba(15, 23, 42, 0.12); +} + +/* Senior QA final: Fabric List card rhythm, filename readability, and zoom toolbar polish. */ +.grid-page { + max-width: min(1180px, calc(100vw - 32px)); + padding-bottom: clamp(52px, 8vh, 86px); +} + +.grid-page .grid-header { + flex-direction: row; + justify-content: space-between; + gap: 18px; +} + +.grid-page .grid-left { + min-width: 0; +} + +.grid-page .grid-title-text, +.grid-page .grid-count-inline, +.grid-page .grid-controls.inline .grid-page-indicator, +.grid-page .audio-label, +.grid-page .media-meta, +.grid-page .media-meta time { + opacity: 1; + text-shadow: none; +} + +.grid-page .grid-controls.inline button, +.grid-page .grid-controls.inline .grid-page-indicator { + min-height: 40px; + border-radius: 999px; + font-weight: 850; +} + +.grid-page .grid-controls.inline button:not(:disabled) { + background: linear-gradient(135deg, var(--tz-action-yellow, #e0a82e), var(--tz-action-yellow-hover, #f0c463)); + border-color: var(--tz-action-yellow, #e0a82e); + color: var(--tz-action-yellow-text, #171101); +} + +.grid-page .grid-controls.inline button:disabled { + opacity: 0.74; +} + +.grid-page .media-grid { + gap: 20px; +} + +.grid-page .media-card { + min-height: 100%; + border-radius: 18px; + overflow: hidden; +} + +.grid-page .media-thumb { + width: 100%; + max-width: none; +} + +.grid-page .media-name { + min-height: 44px; + padding: 10px 14px; + display: block; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-style: normal; + font-weight: 850; + letter-spacing: 0.01em; + line-height: 1.25; +} + +.grid-page .zoom-icon { + top: 12px; + right: 12px; + bottom: auto; + width: 40px; + height: 40px; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 999px; + background: rgba(15, 23, 42, 0.86); + border: 1px solid rgba(255, 255, 255, 0.28); + color: #ffffff; + box-shadow: 0 10px 22px rgba(0, 0, 0, 0.28); +} + +.grid-page .audio-box { + padding: 10px 12px; + gap: 8px; +} + +.grid-page .lb-stage { + width: min(94vw, 1180px); + height: min(86dvh, 820px); + border-radius: 22px; + padding: clamp(12px, 2vw, 22px); + background: rgba(15, 23, 42, 0.96); + border: 1px solid rgba(226, 232, 240, 0.22); + box-shadow: 0 24px 70px rgba(0, 0, 0, 0.42); +} + +.grid-page .lb-img { + max-width: 100%; + max-height: 100%; + object-fit: contain; + transform-origin: center center; +} + +.grid-page .lb-controls { + left: 50%; + right: auto; + bottom: 18px; + transform: translateX(-50%); + display: inline-flex; + align-items: center; + gap: 8px; + padding: 8px; + border-radius: 999px; + background: rgba(15, 23, 42, 0.88); + border: 1px solid rgba(226, 232, 240, 0.22); + box-shadow: 0 14px 34px rgba(0, 0, 0, 0.34); + backdrop-filter: blur(12px); +} + +.grid-page .lb-controls button { + min-width: 42px; + height: 40px; + border-radius: 999px; + border: 1px solid rgba(226, 232, 240, 0.26); + background: rgba(30, 41, 59, 0.92); + color: #f8fafc; + font-weight: 900; + line-height: 1; +} + +.grid-page .lb-controls button:hover:not(:disabled) { + background: rgba(51, 65, 85, 0.98); + transform: translateY(-1px); +} + +.grid-page .lb-controls button:disabled { + opacity: 0.46; +} + +.grid-page .lb-controls .lb-close { + background: linear-gradient(135deg, #ef4444, #b91c1c); + color: #ffffff; + border-color: rgba(248, 113, 113, 0.72); +} + +.grid-page .lb-caption { + left: 18px; + bottom: 18px; + max-width: min(420px, calc(100% - 44px)); + font-size: 14px; + font-weight: 850; + color: #f8fafc; + background: rgba(15, 23, 42, 0.80); + border: 1px solid rgba(226, 232, 240, 0.18); +} + +@media (max-width: 720px) { + .grid-page .grid-header { + flex-direction: column; + align-items: stretch; + } + + .grid-page .grid-controls.inline { + justify-content: center; + flex-wrap: wrap; + } + + .grid-page .lb-controls { + bottom: 12px; + max-width: calc(100% - 24px); + overflow-x: auto; + } + + .grid-page .lb-caption { + display: none; + } +} + +/* Senior QA top-up: List captions must show a clean user-facing filename, not generated storage suffixes. */ +.grid-page .media-name { + min-height: 46px; + padding-inline: 12px; + font-size: clamp(14px, 1.05vw, 17px); + line-height: 1.18; + text-align: center; + display: flex; + align-items: center; + justify-content: center; + white-space: normal; + overflow-wrap: anywhere; + word-break: normal; + hyphens: none; +} + +.grid-page .media-card { + background: var(--tz-ui-surface-raised, rgba(15, 23, 42, 0.94)); +} + +:root[data-theme="light"] .grid-page .media-name { + color: #0f172a; +} + +:root[data-theme="dark"] .grid-page .media-name { + color: #f8fafc; +} + +@media (min-width: 1080px) { + .grid-page .media-grid { + grid-template-columns: repeat(4, minmax(220px, 1fr)); + } +} + + +/* Strict senior QA follow-up: clean List titles and prevent awkward generated-name wrapping. */ +.grid-page .media-name { + min-height: 42px; + padding: 10px 14px; + display: block; + width: 100%; + text-align: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + overflow-wrap: normal; + word-break: normal; + hyphens: manual; + font-size: clamp(13px, 0.95vw, 16px); + line-height: 1.25; +} + +.grid-page .media-audio { + padding: 12px 14px 10px; +} + +.grid-page .audio-box { + min-height: 92px; + border-radius: 18px; +} + +.grid-page .zoom-icon { + top: 10px; + right: 10px; + width: 42px; + height: 42px; +} + +.grid-page .media-grid { + padding-bottom: clamp(42px, 6vh, 72px); +} + +.grid-page .lb-controls { + min-height: 54px; + max-width: min(440px, calc(100vw - 28px)); + overflow: visible; +} + +.grid-page .lb-controls button { + flex: 0 0 auto; + min-width: 42px; +} + +.grid-page .lb-controls button:nth-child(2) { + min-width: 96px; +} + +@media (max-width: 720px) { + .grid-page .media-name { + font-size: 14px; + } + + .grid-page .lb-controls { + gap: 6px; + overflow-x: auto; + } +} + +/* Final senior QA top-up: product-grade List titles and lightbox controls. */ +.grid-page .media-name { + height: 44px; + min-height: 44px; + max-height: 44px; + padding: 10px 14px; + display: flex; + align-items: center; + justify-content: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-transform: none; +} + +.grid-page .media-name::first-letter { + text-transform: uppercase; +} + +.grid-page .lb-controls { + min-height: 52px; + gap: 8px; + padding: 8px; +} + +.grid-page .lb-controls button { + height: 42px; + min-width: 42px; + border-radius: 999px; +} + +.grid-page .lb-controls button:nth-child(2) { + min-width: 96px; +} diff --git a/frontend/src/assets/styles/CrossRouteLayoutCompletion.css b/frontend/src/assets/styles/CrossRouteLayoutCompletion.css new file mode 100644 index 0000000..a367df6 --- /dev/null +++ b/frontend/src/assets/styles/CrossRouteLayoutCompletion.css @@ -0,0 +1,465 @@ +/* Cross-route layout completion. + * + * This final layer standardises the shared application shell without changing + * route state, request handling, navigation destinations, upload/search/chat + * behaviour, or reader actions. Short pages use the remaining viewport space + * so the footer rests at the bottom; long/result pages keep natural document + * flow and place the footer after their content. Chat intentionally remains a + * footer-free, viewport-sized workspace. + */ + +.app-wrapper:not(.app-wrapper--chat) { + min-height: 100dvh; + height: auto; + overflow-x: hidden; + overflow-y: visible; +} + +.app-wrapper:not(.app-wrapper--chat) .main-content { + display: flex; + flex: 1 1 auto; + flex-direction: column; + min-height: 0; + height: auto; + overflow: visible; +} + +.app-wrapper:not(.app-wrapper--chat) .main-content > * { + width: 100%; +} + +.app-wrapper:not(.app-wrapper--chat) .site-footer { + flex: 0 0 auto; + margin-top: auto; +} + +/* Analysis is a normal shell page when idle. Its existing internal panels and + fixed sample drawer continue to work, but the route no longer suppresses the + shared footer or creates a second viewport-height workspace. */ +.app-wrapper--analysis { + height: auto; + overflow: visible; +} + +.app-wrapper--analysis .site-header { + flex: 0 0 auto; +} + +.app-wrapper--analysis .main-content { + height: auto; + flex: 1 1 auto; + overflow: visible; +} + +.app-wrapper--analysis .home-container.analysis-page { + flex: 1 1 auto; + height: auto; + min-height: 0; + overflow-x: hidden; + overflow-y: auto; +} + +/* Short-content routes fill only the shell's available main area. Existing + cards/results still increase the natural page height when content is added. */ +.app-wrapper:not(.app-wrapper--chat) .upload-page, +.app-wrapper:not(.app-wrapper--chat) .grid-page, +.app-wrapper:not(.app-wrapper--chat) .fabric-search, +.app-wrapper:not(.app-wrapper--chat) .reader-landing { + flex: 1 1 auto; + min-height: 0; +} + +.app-wrapper:not(.app-wrapper--chat) .fabric-search { + display: flex; + flex-direction: column; +} + +.app-wrapper:not(.app-wrapper--chat) .fabric-search .fabric-search__body { + flex: 1 0 auto; +} + +/* FabricSearch.css contains a late idle-state viewport minimum that adds a + second near-viewport block inside the shared shell. Neutralise only that + idle/empty state so the footer is visible in one glance; result and preview + states retain natural content height and continue pushing the footer down. */ +.app-wrapper:not(.app-wrapper--chat) + .fabric-search:not(.fabric-search--has-results):not(.fabric-search--previewing) + .fabric-search__body { + min-height: 0; +} + +/* Reader landing responsiveness. The original inline @media object was not a + valid React style rule, so the cards never stacked. */ +.reader-landing__grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +@media (max-width: 768px) { + .reader-landing { + padding: 20px 16px 32px !important; + } + + .reader-landing__header { + margin-bottom: 24px !important; + } + + .reader-landing__grid { + grid-template-columns: minmax(0, 1fr); + gap: 18px !important; + } + + .reader-landing__tile { + min-height: 220px !important; + padding: 30px 24px !important; + } +} + +/* Compact only height-constrained desktop workspaces so their idle/empty state + and footer fit in one glance. Normal and content-rich layouts are unchanged. */ +@media (min-width: 901px) and (max-height: 900px) { + .app-wrapper:not(.app-wrapper--chat) .upload-page { + padding-block: 16px 22px; + } + + .app-wrapper:not(.app-wrapper--chat) .upload-page .upload-card { + min-height: 0; + padding: 18px 22px; + } + + .app-wrapper:not(.app-wrapper--chat) .upload-page .upload-header { + margin-bottom: 14px; + } + + .app-wrapper:not(.app-wrapper--chat) .upload-page .dropzone, + .app-wrapper:not(.app-wrapper--chat) .upload-page .image-preview-wrap { + height: 190px; + } + + .app-wrapper:not(.app-wrapper--chat) .grid-page { + padding-block: 12px 24px; + } + + .app-wrapper:not(.app-wrapper--chat) .grid-page .grid-header { + margin-block: 14px 12px; + } + + .app-wrapper:not(.app-wrapper--chat) .fabric-search:not(.fabric-search--has-results):not(.fabric-search--previewing) + .fabric-search__body { + padding-top: 10px; + padding-bottom: 22px; + } +} + +/* v45: PAN and Aadhaar readers previously nested their own 100vh minimum + inside the shared header/main/footer shell. That extra viewport pushed the + footer below the first screen even when the idle upload card had ample room. + Let the reader route fill the shell's available main area instead, while + preserving natural page growth for crop and result states. */ +.app-wrapper:not(.app-wrapper--chat) .document-reader-page { + box-sizing: border-box; + flex: 1 0 auto; + min-height: 0 !important; + width: 100%; +} + +/* Chat prompt chips: no clipped side pills, no forced single-line overflow. + Mobile gets a readable one-column stack, tablets use two columns and desktop + preserves the established four-column row. */ +.app-wrapper--chat .page-root .empty-state-large .chips, +.app-wrapper--chat .page-root .suggestion-chips .chips-row { + width: min(100%, 980px); + max-width: 100%; +} + +.app-wrapper--chat .page-root .empty-state-large .chip, +.app-wrapper--chat .page-root .suggestion-chips .chip { + min-width: 0; + max-width: 100%; + white-space: normal; + overflow-wrap: anywhere; + text-align: center; +} + +@media (min-width: 769px) and (max-width: 1100px) { + .app-wrapper--chat .page-root .empty-state-large .chips { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 768px) { + .app-wrapper--chat .page-root .empty-state-large { + width: 100%; + padding-inline: 0; + } + + .app-wrapper--chat .page-root .empty-state-large .chips, + .app-wrapper--chat .page-root .suggestion-chips .chips-row { + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: 10px; + width: min(100%, 430px); + margin-inline: auto; + padding-inline: 0; + } + + .app-wrapper--chat .page-root .empty-state-large .chip, + .app-wrapper--chat .page-root .suggestion-chips .chip { + width: 100%; + min-height: 46px; + padding: 10px 14px; + } + + /* The desktop theme switch stays in the desktop header. Mobile receives the + same live control at the end of the full-screen navigation menu. */ + .site-header > .header-right { + display: none; + } + + .site-header .header-nav ul { + align-items: stretch; + gap: 10px; + padding: 82px 20px max(22px, env(safe-area-inset-bottom)); + background: var(--tz-ui-surface-raised, rgba(255, 255, 255, 0.98)); + color: var(--tz-ui-text, #0f172a); + border: 0; + box-shadow: none; + backdrop-filter: blur(18px); + -webkit-backdrop-filter: blur(18px); + } + + :root[data-theme="dark"] .site-header .header-nav ul { + background: rgba(10, 15, 29, 0.985); + color: #f8fafc; + } + + .site-header .header-nav[data-open="false"] ul { + transform: translateY(-100%); + opacity: 0; + visibility: hidden; + pointer-events: none; + } + + .site-header .header-nav[data-open="true"] ul { + transform: translateY(0); + opacity: 1; + visibility: visible; + pointer-events: auto; + } + + .site-header .mobile-menu__brand { + position: absolute; + top: max(17px, env(safe-area-inset-top)); + left: 20px; + display: flex; + align-items: center; + gap: 10px; + min-height: 42px; + } + + .site-header .mobile-menu__brand-mark { + display: inline-grid; + width: 36px; + height: 36px; + place-items: center; + border-radius: 10px; + background: #2f6bff; + color: #ffffff; + font-size: 12px; + font-weight: 900; + letter-spacing: 0.04em; + box-shadow: 0 10px 24px rgba(47, 107, 255, 0.24); + } + + .site-header .mobile-menu__brand-name { + color: inherit; + font-family: var(--tz-font-display, inherit); + font-size: 20px; + font-weight: 900; + letter-spacing: -0.02em; + } + + .site-header .mobile-menu__route { + width: min(100%, 520px); + margin-inline: auto; + } + + .site-header .mobile-menu__route a { + display: flex; + min-height: 52px; + align-items: center; + justify-content: center; + padding: 12px 18px; + border: 1px solid var(--tz-ui-border, rgba(15, 23, 42, 0.16)); + border-radius: 14px; + background: var(--tz-ui-surface-subtle, rgba(248, 250, 252, 0.78)); + color: var(--tz-ui-text, #0f172a); + font-size: clamp(18px, 4.4vw, 22px); + font-weight: 850; + line-height: 1.1; + text-align: center; + text-transform: capitalize; + } + + :root[data-theme="dark"] .site-header .mobile-menu__route a { + background: rgba(30, 41, 59, 0.78); + border-color: rgba(226, 232, 240, 0.2); + color: #f8fafc; + } + + .site-header .mobile-menu__route a.active, + :root[data-theme="dark"] .site-header .mobile-menu__route a.active { + background: linear-gradient(135deg, #b7791f, #d09128); + border-color: rgba(183, 121, 31, 0.72); + color: #ffffff; + box-shadow: 0 12px 28px rgba(183, 121, 31, 0.22); + } + + .site-header .mobile-menu__theme { + display: grid; + width: min(100%, 520px); + gap: 8px; + margin: auto auto 0; + padding-top: 16px; + border-top: 1px solid var(--tz-ui-border, rgba(15, 23, 42, 0.16)); + } + + .site-header .mobile-menu__theme-label { + color: var(--tz-ui-text-muted, #64748b); + font-size: 12px; + font-weight: 800; + letter-spacing: 0.12em; + text-transform: uppercase; + } + + .site-header .mobile-menu__theme-toggle { + display: flex; + width: 100%; + min-height: 50px; + align-items: center; + justify-content: center; + gap: 10px; + padding: 10px 16px; + border: 1px solid var(--tz-ui-border, rgba(15, 23, 42, 0.16)); + border-radius: 14px; + background: var(--tz-ui-control-bg, #ffffff); + color: var(--tz-ui-text, #0f172a); + font: inherit; + font-weight: 800; + } + + :root[data-theme="dark"] .site-header .mobile-menu__theme-toggle { + background: rgba(30, 41, 59, 0.92); + border-color: rgba(226, 232, 240, 0.22); + color: #f8fafc; + } + + .site-header .mobile-menu__theme-icon { + display: inline-flex; + width: 22px; + height: 22px; + } + + .site-header .mobile-menu__theme-icon svg { + width: 100%; + height: 100%; + } + + .site-header .hamburger span { + background: var(--tz-ui-text, #0f172a); + } + + :root[data-theme="dark"] .site-header .hamburger span { + background: #f8fafc; + } +} + +@media (min-width: 769px) { + .mobile-menu__brand, + .mobile-menu__theme { + display: none; + } +} + +/* v39: keep the mobile appearance control with the route list instead of + pushing it to the bottom of the viewport. The menu remains independently + scrollable on short devices through HomeViewportNavigationFix.css. */ +@media (max-width: 768px) { + .site-header .mobile-menu__theme { + margin: 6px auto 0; + padding-top: 12px; + } +} + +/* v46: Analysis uses the same footer component as every other route. Explicitly + remove route-level dimming and restore the shared footer contrast so the + footer is not washed out beneath the Analysis workspace/drawer layers. */ +.app-wrapper--analysis .site-footer { + position: relative; + z-index: 600; + opacity: 1 !important; + visibility: visible !important; + filter: none !important; + mix-blend-mode: normal !important; + isolation: isolate; +} + +.app-wrapper--analysis + .site-footer + :is( + .footer__brand, + .footer__logo, + .footer__title, + .footer__desc, + .footer__github, + .footer__cols, + .footer__col, + .footer__col h4, + .footer__col a, + .footer__rule, + .footer__copyright, + .footer__copyright a + ) { + opacity: 1 !important; + visibility: visible !important; + filter: none !important; + mix-blend-mode: normal !important; +} + +:root[data-theme="dark"] .app-wrapper--analysis .site-footer { + background: #0c1220 !important; + border-top-color: rgba(148, 163, 184, 0.28) !important; + box-shadow: 0 -8px 28px rgba(2, 6, 23, 0.28) !important; + color: #d2d8e3 !important; +} + +:root[data-theme="dark"] + .app-wrapper--analysis + .site-footer + :is(.footer__title, .footer__col h4) { + color: #f1f5ff !important; + -webkit-text-fill-color: #f1f5ff !important; +} + +:root[data-theme="dark"] + .app-wrapper--analysis + .site-footer + :is( + .footer__desc, + .footer__github, + .footer__col a, + .footer__copyright, + .footer__copyright a + ) { + color: #c7d3e3 !important; + -webkit-text-fill-color: #c7d3e3 !important; +} + +/* v53: keep the fixed Analysis sample drawer above the shared footer. The + Analysis page deliberately isolates its internal layers, so the footer's + route-level z-index could otherwise cover the drawer near the viewport + bottom. Lift only the Analysis main-content stacking context; document flow, + footer placement, drawer scrolling and all component behavior are unchanged. */ +.app-wrapper--analysis .main-content { + position: relative; + z-index: 610; +} diff --git a/frontend/src/assets/styles/DescriptionBox.css b/frontend/src/assets/styles/DescriptionBox.css index 4e44093..38e9845 100644 --- a/frontend/src/assets/styles/DescriptionBox.css +++ b/frontend/src/assets/styles/DescriptionBox.css @@ -299,3 +299,119 @@ opacity: 1 ; visibility: visible ; } + +/* QA follow-up: Prev/Next should visually share the same row level as Short/Long. */ +.analysis-page .description-wrapper .nav-buttons { + margin-top: 18px; + min-height: 46px; + display: flex; + align-items: center; + justify-content: center; + gap: 12px; + transform: translateY(2px); +} + +.analysis-page .description-wrapper .nav-buttons .nav-btn { + min-height: 44px; + min-width: 132px; + border-radius: 999px; + border: 1px solid var(--tz-turmeric, #e0a82e); + background: linear-gradient(135deg, var(--tz-turmeric, #e0a82e), var(--tz-turmeric-soft, #f59e0b)); + color: #ffffff; + font-weight: 850; + box-shadow: 0 10px 24px rgba(224, 168, 46, 0.24); +} + +:root[data-theme="dark"] .analysis-page .description-wrapper .nav-buttons .nav-btn { + color: #171101; +} + +.analysis-page .description-wrapper .nav-buttons .nav-btn.disabled, +.analysis-page .description-wrapper .nav-buttons .nav-btn:disabled { + opacity: 0.56; + cursor: not-allowed; + box-shadow: none; +} + +/* Senior QA final: Prev/Next match Analysis buttons and sit on the same visual segment. */ +.analysis-page .description-wrapper .nav-buttons { + min-height: 46px; + display: flex; + align-items: center; + justify-content: center; + gap: 12px; + margin-top: 18px; + transform: translateY(2px); +} + +.analysis-page .description-wrapper .nav-buttons .nav-btn { + min-height: 44px; + min-width: 142px; + padding: 0 18px; + border-radius: 999px; + border: 1px solid var(--tz-action-yellow, #e0a82e); + background: linear-gradient(135deg, var(--tz-action-yellow, #e0a82e), var(--tz-action-yellow-hover, #f0c463)); + color: var(--tz-action-yellow-text, #171101); + font-weight: 900; + box-shadow: 0 10px 24px rgba(224, 168, 46, 0.24); +} + +.analysis-page .description-wrapper .nav-buttons .nav-btn:not(:disabled):not(.disabled):hover { + transform: translateY(-1px); + box-shadow: 0 14px 30px rgba(224, 168, 46, 0.30); +} + +.analysis-page .description-wrapper .nav-buttons .nav-btn.disabled, +.analysis-page .description-wrapper .nav-buttons .nav-btn:disabled { + opacity: 0.68; + color: rgba(23, 17, 1, 0.72); + box-shadow: none; +} + +/* Senior QA top-up: Prev/Next disabled state remains intentional and aligned. */ +.analysis-page .description-wrapper .nav-buttons { + margin-top: 18px; + min-height: 46px; +} + +:root[data-theme="light"] .analysis-page .description-wrapper .nav-buttons .nav-btn, +:root[data-theme="dark"] .analysis-page .description-wrapper .nav-buttons .nav-btn { + color: #171101; +} + +.analysis-page .description-wrapper .nav-buttons .nav-btn.disabled, +.analysis-page .description-wrapper .nav-buttons .nav-btn:disabled { + background: linear-gradient(135deg, rgba(224, 168, 46, 0.58), rgba(240, 196, 99, 0.58)); + border-color: rgba(224, 168, 46, 0.66); + color: rgba(23, 17, 1, 0.72); +} + + +/* Strict senior QA follow-up: keep Prev/Next visually synced with Short/Long. */ +.analysis-page .description-wrapper .nav-buttons { + margin-top: 18px; + min-height: 46px; + align-items: center; +} + +.analysis-page .description-wrapper .nav-buttons .nav-btn, +.analysis-page .description-wrapper .nav-buttons .nav-btn.disabled, +.analysis-page .description-wrapper .nav-buttons .nav-btn:disabled { + min-height: 44px; + min-width: 142px; + border-radius: 999px; + font-weight: 900; +} + +/* Analysis-page layout: navigation belongs above the analysis result card. + These direct-child rules intentionally override the legacy below-card order + without affecting DescriptionBox in any other page or context. */ +.analysis-page .description-wrapper > .nav-buttons { + order: 1; + margin: 0 0 18px; + transform: none; +} + +.analysis-page .description-wrapper > .description-box { + order: 2; +} diff --git a/frontend/src/assets/styles/DrawerToggle.css b/frontend/src/assets/styles/DrawerToggle.css index c99ac18..82ddd8b 100644 --- a/frontend/src/assets/styles/DrawerToggle.css +++ b/frontend/src/assets/styles/DrawerToggle.css @@ -56,3 +56,34 @@ .analysis-page:not(.drawer-open) .drawer-toggle-button { border-radius: 14px 0 0 14px; } + +/* Final senior QA top-up: drawer handle should not dominate the analysis page. */ +.analysis-page .drawer-toggle-wrapper { + right: clamp(248px, 19vw, 272px); +} + +.analysis-page .drawer-toggle-button { + width: 30px; + height: 58px; + border-radius: 12px 0 0 12px; + background: linear-gradient(135deg, #f0b429, #d97706); + border: 1px solid rgba(120, 53, 15, 0.28); + color: #171101; + box-shadow: 0 10px 24px rgba(15, 23, 42, 0.22); + font-size: 22px; +} + +/* Final micro top-up: drawer handle should follow theme with the drawer. */ +:root[data-theme="light"] .analysis-page .drawer-toggle-button { + background: rgba(255, 255, 255, 0.96); + color: #b45309; + border-color: rgba(15, 23, 42, 0.16); + box-shadow: 0 10px 24px rgba(15, 23, 42, 0.16); +} + +:root[data-theme="dark"] .analysis-page .drawer-toggle-button { + background: linear-gradient(135deg, #f0b429, #d97706); + color: #171101; + border-color: rgba(120, 53, 15, 0.28); + box-shadow: 0 10px 24px rgba(15, 23, 42, 0.22); +} diff --git a/frontend/src/assets/styles/EmptyState.css b/frontend/src/assets/styles/EmptyState.css index cf26569..0193d60 100644 --- a/frontend/src/assets/styles/EmptyState.css +++ b/frontend/src/assets/styles/EmptyState.css @@ -64,4 +64,193 @@ .empty-state-large .chip:nth-child(even) { background: #aeafb23f; border-color: #e5e7eb; -} \ No newline at end of file +} +/* Senior QA: keep chat landing copy and prompt chips readable after theme changes. */ +.page-root .empty-state-large h3 { + color: var(--tz-ui-text, var(--tz-cotton, #f8fafc)); + opacity: 1; + text-shadow: none; +} + +.page-root .empty-state-large p { + color: var(--tz-ui-text-muted, var(--tz-muted, #cbd5e1)); + opacity: 1; + text-shadow: none; +} + +.page-root .empty-state-large .chip, +.page-root .empty-state-large .chip:nth-child(odd), +.page-root .empty-state-large .chip:nth-child(even) { + color: var(--tz-ui-text, #0f172a); + background: var(--tz-ui-control-bg, rgba(255, 255, 255, 0.94)); + border-color: var(--tz-ui-border, rgba(15, 23, 42, 0.14)); + opacity: 1; + text-shadow: none; +} + +:root[data-theme="dark"] .page-root .empty-state-large .chip, +:root[data-theme="dark"] .page-root .empty-state-large .chip:nth-child(odd), +:root[data-theme="dark"] .page-root .empty-state-large .chip:nth-child(even) { + color: #f8fafc; + background: rgba(30, 41, 59, 0.96); + border-color: rgba(226, 232, 240, 0.24); +} + +:root[data-theme="light"] .page-root .empty-state-large .chip, +:root[data-theme="light"] .page-root .empty-state-large .chip:nth-child(odd), +:root[data-theme="light"] .page-root .empty-state-large .chip:nth-child(even) { + color: #0f172a; + background: rgba(255, 255, 255, 0.96); + border-color: rgba(15, 23, 42, 0.14); +} + +/* Senior visual-system pass: prompt chips should never look blank or disabled by accident. */ +.page-root .empty-state-large .chips { + gap: 12px; +} + +.page-root .empty-state-large .chip, +.page-root .empty-state-large .chip:nth-child(odd), +.page-root .empty-state-large .chip:nth-child(even) { + min-height: 40px; + color: var(--tz-ui-text, #0f172a); + background: var(--tz-ui-control-bg, rgba(255, 255, 255, 0.96)); + border-color: var(--tz-ui-border, rgba(15, 23, 42, 0.14)); + font-weight: 800; + box-shadow: 0 10px 26px rgba(15, 23, 42, 0.08); +} + +.page-root .empty-state-large .chip:disabled { + opacity: 0.72; + color: var(--tz-ui-text-muted, #64748b); +} + +:root[data-theme="dark"] .page-root .empty-state-large .chip, +:root[data-theme="dark"] .page-root .empty-state-large .chip:nth-child(odd), +:root[data-theme="dark"] .page-root .empty-state-large .chip:nth-child(even) { + color: var(--tz-ui-text, #f8fafc); + background: var(--tz-ui-control-bg, rgba(30, 41, 59, 0.96)); + border-color: var(--tz-ui-border, rgba(226, 232, 240, 0.24)); +} + +/* QA follow-up: keep the four landing prompt buttons on one row on desktop and make each state visually distinct. */ +.page-root .empty-state-large .chips { + width: min(980px, 100%); + margin: 0 auto; + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 12px; + align-items: stretch; + justify-content: center; +} + +.page-root .empty-state-large .chip, +.page-root .empty-state-large .chip:nth-child(odd), +.page-root .empty-state-large .chip:nth-child(even) { + width: 100%; + min-height: 44px; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 10px 14px; + white-space: normal; + line-height: 1.18; + text-align: center; + font-weight: 850; +} + +.page-root .empty-state-large .chip:nth-child(1) { + background: #fff7ed; + border-color: #fed7aa; + color: #7c2d12; +} + +.page-root .empty-state-large .chip:nth-child(2) { + background: #eef2ff; + border-color: #c7d2fe; + color: #312e81; +} + +.page-root .empty-state-large .chip:nth-child(3) { + background: #ecfdf5; + border-color: #bbf7d0; + color: #064e3b; +} + +.page-root .empty-state-large .chip:nth-child(4) { + background: #fdf2f8; + border-color: #fbcfe8; + color: #831843; +} + +:root[data-theme="dark"] .page-root .empty-state-large .chip:nth-child(1) { + background: rgba(251, 146, 60, 0.18); + border-color: rgba(251, 146, 60, 0.42); + color: #fed7aa; +} + +:root[data-theme="dark"] .page-root .empty-state-large .chip:nth-child(2) { + background: rgba(99, 102, 241, 0.2); + border-color: rgba(129, 140, 248, 0.46); + color: #c7d2fe; +} + +:root[data-theme="dark"] .page-root .empty-state-large .chip:nth-child(3) { + background: rgba(16, 185, 129, 0.18); + border-color: rgba(52, 211, 153, 0.42); + color: #bbf7d0; +} + +:root[data-theme="dark"] .page-root .empty-state-large .chip:nth-child(4) { + background: rgba(236, 72, 153, 0.2); + border-color: rgba(244, 114, 182, 0.46); + color: #fbcfe8; +} + +@media (max-width: 960px) { + .page-root .empty-state-large .chips { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 560px) { + .page-root .empty-state-large .chips { + grid-template-columns: 1fr; + } +} + +/* Senior QA final: desktop prompts must look intentional at browser zoom levels. */ +.page-root .empty-state-large { + width: min(980px, 100%); + padding-inline: 8px; +} + +.page-root .empty-state-large .chips { + grid-template-columns: repeat(4, minmax(150px, 1fr)); + align-items: stretch; +} + +.page-root .empty-state-large .chip { + min-width: 0; + min-height: var(--tz-control-height, 44px); + border-radius: var(--tz-control-radius, 999px); + border-width: 1px; + box-shadow: 0 10px 22px rgba(15, 23, 42, 0.10); +} + +.page-root .empty-state-large .chip:hover:not(:disabled) { + transform: translateY(-2px); + box-shadow: 0 16px 30px rgba(15, 23, 42, 0.14); +} + +@media (min-width: 961px) and (max-width: 1180px) { + .page-root .empty-state-large .chips { + grid-template-columns: repeat(4, minmax(132px, 1fr)); + gap: 10px; + } + + .page-root .empty-state-large .chip { + padding-inline: 10px; + font-size: 13px; + } +} diff --git a/frontend/src/assets/styles/FabricChat.css b/frontend/src/assets/styles/FabricChat.css index 8f91696..f537727 100644 --- a/frontend/src/assets/styles/FabricChat.css +++ b/frontend/src/assets/styles/FabricChat.css @@ -215,6 +215,14 @@ margin: 8px auto 0; } +/* Keep server failures readable after lazy-loaded Chat styles are inserted. */ +.app-wrapper--chat .page-root .error[role="alert"], +.app-wrapper--chat .page-root .error[role="alert"] * { + color: #ffffff !important; + -webkit-text-fill-color: #ffffff !important; + text-shadow: none !important; +} + @media (max-width: 720px) { .page-root { padding: 72px 12px 28px; @@ -390,3 +398,305 @@ padding: 0 ; } } + +/* Senior QA patch: Chat text contrast after theme toggle. + Keeps empty-state heading/subtitle, topbar labels, composer text and action labels readable. */ +.page-root .chat-card-topbar, +.page-root .chat-body, +.page-root .empty-state-large, +.page-root .composer { + color: var(--tz-ui-text, var(--tz-cotton, #f8fafc)); +} + +.page-root .chat-card-title .main, +.page-root .empty-state-large h3 { + color: var(--tz-ui-text, #f8fafc); + opacity: 1; + text-shadow: none; +} + +.page-root .empty-state-large p, +.page-root .audio-label, +.page-root .stop-text { + color: var(--tz-ui-text-muted, #cbd5e1); + opacity: 1; + text-shadow: none; +} + +.page-root .chat-card-actions .btn, +.page-root .chat-card-actions .btn span, +.page-root .chat-card-actions .chat-action-icon { + color: inherit; + opacity: 1; + text-shadow: none; +} + +.page-root .chat-card-actions .btn-secondary { + background: var(--tz-turmeric, #e0a82e); + border-color: var(--tz-turmeric, #e0a82e); + color: #171101; +} + +.page-root .chat-card-actions .btn-ghost { + background: var(--tz-ui-control-bg, rgba(30, 41, 59, 0.94)); + border-color: var(--tz-ui-border, rgba(226, 232, 240, 0.24)); + color: var(--tz-ui-text, #f8fafc); +} + +:root[data-theme="light"] .page-root .chat-card-title .main, +:root[data-theme="light"] .page-root .empty-state-large h3, +:root[data-theme="light"] .page-root .chat-card-actions .btn-ghost { + color: #0f172a; +} + +:root[data-theme="light"] .page-root .empty-state-large p, +:root[data-theme="light"] .page-root .audio-label, +:root[data-theme="light"] .page-root .stop-text { + color: #475569; +} + +:root[data-theme="light"] .page-root .chat-card-actions .btn-ghost { + background: #ffffff; + border-color: rgba(15, 23, 42, 0.14); +} + +/* Senior QA: chat readability and bottom-control clipping fixes without forced-priority rules. */ +.page-root .chat-card-topbar, +.page-root .chat-body, +.page-root .empty-state-large, +.page-root .composer { + color: var(--tz-ui-text, var(--tz-cotton, #f8fafc)); +} + +.page-root .chat-card-title .main, +.page-root .empty-state-large h3 { + color: var(--tz-ui-text, #f8fafc); + opacity: 1; + text-shadow: none; +} + +.page-root .empty-state-large p, +.page-root .audio-label, +.page-root .stop-text { + color: var(--tz-ui-text-muted, #cbd5e1); + opacity: 1; + text-shadow: none; +} + +.page-root .chat-card-actions .btn, +.page-root .chat-card-actions .btn span, +.page-root .chat-card-actions .chat-action-icon { + color: inherit; + opacity: 1; + text-shadow: none; +} + +.page-root .chat-card-actions .btn-secondary { + background: var(--tz-turmeric, #e0a82e); + border-color: var(--tz-turmeric, #e0a82e); + color: #171101; +} + +.page-root .chat-card-actions .btn-ghost { + background: var(--tz-ui-control-bg, rgba(30, 41, 59, 0.94)); + border-color: var(--tz-ui-border, rgba(226, 232, 240, 0.24)); + color: var(--tz-ui-text, #f8fafc); +} + +.page-root .chat-body { + padding-bottom: max(12px, env(safe-area-inset-bottom)); +} + +.page-root .chat-scroll-area { + padding-bottom: 24px; +} + +.page-root .composer { + flex: 0 0 auto; + padding: 8px 8px max(14px, env(safe-area-inset-bottom)); + border-radius: 16px 16px 0 0; +} + +.page-root .composer-audio-row { + margin-top: 8px; + margin-right: 8px; + padding-bottom: 2px; +} + +.page-root .record-btn, +.page-root .audio-label, +.page-root .stop-text { + line-height: 1.2; +} + +:root[data-theme="light"] .page-root .chat-card-title .main, +:root[data-theme="light"] .page-root .empty-state-large h3, +:root[data-theme="light"] .page-root .chat-card-actions .btn-ghost { + color: #0f172a; +} + +:root[data-theme="light"] .page-root .empty-state-large p, +:root[data-theme="light"] .page-root .audio-label, +:root[data-theme="light"] .page-root .stop-text { + color: #475569; +} + +:root[data-theme="light"] .page-root .chat-card-actions .btn-ghost { + background: #ffffff; + border-color: rgba(15, 23, 42, 0.14); +} + +/* Senior visual-system pass: chat should read as one stable product surface. */ +.page-root { + min-height: calc(100dvh - 64px); + background: var(--tz-ui-page, var(--tz-app-bg)); + color: var(--tz-ui-text, var(--tz-cotton, #f8fafc)); +} + +.page-root .chat-card-topbar { + width: min(1048px, calc(100vw - 32px)); + margin: clamp(10px, 2vh, 18px) auto 12px; + padding: 12px 14px; + background: var(--tz-ui-surface, rgba(15, 23, 42, 0.78)); + border: 1px solid var(--tz-ui-border, rgba(226, 232, 240, 0.2)); + border-radius: 18px; + box-shadow: var(--tz-ui-shadow-panel, 0 18px 48px rgba(0, 0, 0, 0.22)); +} + +.page-root .chat-body { + width: min(1048px, calc(100vw - 32px)); + height: auto; + min-height: calc(100dvh - 162px); + margin: 0 auto; + display: flex; + flex-direction: column; +} + +.page-root.is-empty .chat-scroll-area { + display: flex; + flex-direction: column; + justify-content: flex-start; + padding-top: clamp(24px, 6vh, 66px); + padding-bottom: 20px; +} + +.page-root .empty-state-large h3 { + color: var(--tz-ui-text, #f8fafc); + font-size: clamp(22px, 2.4vw, 30px); + letter-spacing: -0.025em; +} + +.page-root .empty-state-large p { + color: var(--tz-ui-text-muted, #cbd5e1); + font-size: 15px; +} + +.page-root .chat-card-actions .btn { + min-width: 0; + white-space: nowrap; +} + +.page-root .composer { + width: 100%; + margin: auto auto 0; + background: var(--tz-ui-surface, rgba(15, 23, 42, 0.78)); + border: 1px solid var(--tz-ui-border, rgba(226, 232, 240, 0.2)); + box-shadow: var(--tz-ui-shadow-panel, 0 18px 48px rgba(0, 0, 0, 0.22)); +} + +.page-root .composer-audio-row { + justify-content: flex-end; + flex-wrap: wrap; + gap: 10px; + color: var(--tz-ui-text-muted, #cbd5e1); +} + +:root[data-theme="light"] .page-root .chat-card-topbar, +:root[data-theme="light"] .page-root .composer { + background: var(--tz-ui-surface-raised, rgba(255, 255, 255, 0.96)); + border-color: var(--tz-ui-border, rgba(15, 23, 42, 0.14)); +} + +@media (max-height: 760px) { + .page-root.is-empty .chat-scroll-area { + padding-top: 18px; + } + + .page-root .empty-state-large h3 { + font-size: 22px; + } +} + +/* Senior QA final: stabilize chat layout on 1366x768 and 125% browser zoom. */ +.page-root { + padding-bottom: max(18px, env(safe-area-inset-bottom)); +} + +.page-root .chat-card-topbar { + flex: 0 0 auto; +} + +.page-root .chat-body { + min-height: min(680px, calc(100dvh - 154px)); + padding-bottom: 6px; +} + +.page-root.is-empty .chat-scroll-area { + justify-content: center; + padding-top: clamp(18px, 4.4vh, 42px); + padding-bottom: clamp(16px, 3vh, 34px); +} + +.page-root .chat-card-actions .btn { + min-height: var(--tz-control-height, 44px); + border-radius: var(--tz-control-radius, 999px); + font-weight: 850; +} + +.page-root .chat-card-actions .btn-secondary:hover:not(:disabled), +.page-root .chat-card-actions .btn-ghost:hover:not(:disabled) { + transform: translateY(-1px); +} + +@media (max-height: 760px) { + .page-root .chat-card-topbar { + margin-top: 8px; + margin-bottom: 8px; + padding-block: 10px; + } + + .page-root .chat-body { + min-height: calc(100dvh - 144px); + } + + .page-root.is-empty .chat-scroll-area { + justify-content: flex-start; + } +} + +/* Final senior QA top-up: make the Chat empty state feel intentional, not empty. */ +.page-root.is-empty .chat-scroll-area { + justify-content: center; + padding-top: clamp(22px, 4vh, 48px); + padding-bottom: clamp(22px, 4vh, 48px); +} + +.page-root .empty-state-large { + max-width: 980px; + margin-inline: auto; +} + +.page-root .empty-state-large .sample-prompts, +.page-root .sample-prompts { + gap: clamp(10px, 1.2vw, 16px); +} + +.page-root .chat-body { + min-height: min(700px, calc(100dvh - 150px)); +} + +@media (max-height: 760px) { + .page-root .chat-body { + min-height: calc(100dvh - 138px); + } +} diff --git a/frontend/src/assets/styles/FabricSearch.css b/frontend/src/assets/styles/FabricSearch.css index 75391f8..a1c415e 100644 --- a/frontend/src/assets/styles/FabricSearch.css +++ b/frontend/src/assets/styles/FabricSearch.css @@ -1522,9 +1522,16 @@ /* Do not hide the search UI after a failed request, and keep failures visibly red even when the global theme overlay styles paragraphs/buttons. */ .fabric-search__error { - color: var(--color-red) ; - background: rgba(239, 68, 68, 0.12) ; - border: 1px solid rgba(239, 68, 68, 0.34) ; + color: #ffffff !important; + -webkit-text-fill-color: #ffffff !important; + background: #dc2626 !important; + border: 1px solid #991b1b !important; +} + +.fabric-search__error * { + color: #ffffff !important; + -webkit-text-fill-color: #ffffff !important; + text-shadow: none !important; } .result-card__zoom-btn, @@ -2412,3 +2419,1447 @@ max-height: calc(100dvh - 126px) ; } } + +/* Senior QA patch: Search page visibility + settings dropdown layout. + Fixes low-contrast category/database labels and prevents the settings menu + from covering original/cropped images in image-search preview mode. */ +.fabric-search .category-picker, +.fabric-search .db-panel, +.fabric-search .settings-panel__toggle, +.fabric-search .settings-panel__content { + color: var(--tz-ui-text, var(--color-text, #f8fafc)); +} + +.fabric-search .category-picker__title, +.fabric-search .db-panel__label { + color: var(--tz-ui-text-muted, #cbd5e1); + opacity: 1; + text-shadow: none; +} + +.fabric-search .category-picker__toggle-all { + color: var(--tz-green, var(--color-green, #22c55e)); + opacity: 1; +} + +.fabric-search .settings-panel__toggle, +.fabric-search .settings-panel__toggle span { + color: var(--tz-ui-text, var(--color-text, #f8fafc)); + opacity: 1; + text-shadow: none; +} + +.fabric-search .settings-panel__content .db-panel { + background: var(--tz-ui-surface-raised, var(--color-surface, rgba(20, 16, 12, 0.94))); + border-color: var(--tz-ui-border, var(--color-border, rgba(255, 255, 255, 0.12))); + box-shadow: 0 18px 42px rgba(0, 0, 0, 0.24); +} + +.fabric-search .db-panel__btn { + opacity: 1; + text-shadow: none; +} + +.fabric-search.fabric-search--previewing .fabric-search__top-bar { + align-items: flex-start; + justify-content: flex-end; + margin-bottom: 18px; + min-height: auto; + position: relative; + z-index: 30; +} + +.fabric-search.fabric-search--previewing .fabric-search__settings { + width: 100%; + display: flex; + justify-content: flex-end; +} + +.fabric-search.fabric-search--previewing .settings-panel { + display: flex; + flex-direction: column; + align-items: flex-end; + max-width: min(360px, 100%); +} + +.fabric-search.fabric-search--previewing .settings-panel__content { + position: static; + margin-top: 10px; + min-width: min(340px, calc(100vw - 48px)); + max-width: 100%; +} + +.fabric-search.fabric-search--previewing .image-preview { + position: relative; + z-index: 1; +} + +:root[data-theme="light"] .fabric-search .category-picker__title, +:root[data-theme="light"] .fabric-search .db-panel__label, +:root[data-theme="light"] .fabric-search .settings-panel__toggle, +:root[data-theme="light"] .fabric-search .settings-panel__toggle span { + color: #0f172a; +} + +:root[data-theme="light"] .fabric-search .settings-panel__content .db-panel { + background: #ffffff; + border-color: rgba(15, 23, 42, 0.14); +} + +@media (max-width: 760px) { + .fabric-search.fabric-search--previewing .fabric-search__settings, + .fabric-search.fabric-search--previewing .settings-panel { + align-items: stretch; + } + + .fabric-search.fabric-search--previewing .settings-panel__toggle, + .fabric-search.fabric-search--previewing .settings-panel__content { + width: 100%; + } +} + +/* Senior QA: Search visibility + image preview layout guards after component split. */ +.fabric-search:not(.fabric-search--has-results) .fabric-search__body { + padding-top: clamp(48px, 7vh, 88px); +} + +.fabric-search .category-picker, +.fabric-search .db-panel, +.fabric-search .settings-panel__toggle, +.fabric-search .settings-panel__content { + color: var(--tz-ui-text, var(--color-text, #f8fafc)); +} + +.fabric-search .category-picker__title, +.fabric-search .db-panel__label, +.fabric-search .limit-slider__label, +.fabric-search .hero__subtitle, +.fabric-search .hero__divider { + color: var(--tz-ui-text-muted, #cbd5e1); + opacity: 1; + text-shadow: none; +} + +.fabric-search .category-picker__toggle-all { + color: var(--tz-green, var(--color-green, #22c55e)); + opacity: 1; +} + +.fabric-search .settings-panel__toggle, +.fabric-search .settings-panel__toggle span { + color: var(--tz-ui-text, var(--color-text, #f8fafc)); + opacity: 1; + text-shadow: none; +} + +.fabric-search .settings-panel__content .db-panel { + background: var(--tz-ui-surface-raised, var(--color-surface, rgba(20, 16, 12, 0.94))); + border-color: var(--tz-ui-border, var(--color-border, rgba(255, 255, 255, 0.12))); + box-shadow: 0 18px 42px rgba(0, 0, 0, 0.24); +} + +.fabric-search .db-panel__btn { + opacity: 1; + text-shadow: none; +} + +.fabric-search.fabric-search--previewing .fabric-search__top-bar { + align-items: flex-start; + justify-content: flex-end; + margin-bottom: 18px; + min-height: auto; + position: relative; + z-index: 30; +} + +.fabric-search.fabric-search--previewing .fabric-search__settings { + width: 100%; + display: flex; + justify-content: flex-end; +} + +.fabric-search.fabric-search--previewing .settings-panel { + display: flex; + flex-direction: column; + align-items: flex-end; + max-width: min(360px, 100%); +} + +.fabric-search.fabric-search--previewing .settings-panel__content { + position: static; + margin-top: 10px; + min-width: min(340px, calc(100vw - 48px)); + max-width: 100%; +} + +.fabric-search.fabric-search--previewing .image-preview { + position: relative; + z-index: 1; +} + +:root[data-theme="light"] .fabric-search .category-picker__title, +:root[data-theme="light"] .fabric-search .db-panel__label, +:root[data-theme="light"] .fabric-search .settings-panel__toggle, +:root[data-theme="light"] .fabric-search .settings-panel__toggle span, +:root[data-theme="light"] .fabric-search .limit-slider__label, +:root[data-theme="light"] .fabric-search .hero__subtitle, +:root[data-theme="light"] .fabric-search .hero__divider { + color: #0f172a; +} + +:root[data-theme="light"] .fabric-search .settings-panel__content .db-panel { + background: #ffffff; + border-color: rgba(15, 23, 42, 0.14); +} + +@media (max-width: 760px) { + .fabric-search.fabric-search--previewing .fabric-search__settings, + .fabric-search.fabric-search--previewing .settings-panel { + align-items: stretch; + } + + .fabric-search.fabric-search--previewing .settings-panel__toggle, + .fabric-search.fabric-search--previewing .settings-panel__content { + width: 100%; + } +} + +/* Senior visual-system pass: search layout/readability polish after component split. */ +.fabric-search { + color: var(--tz-ui-text, var(--color-text, #f8fafc)); + background: var(--tz-ui-page, var(--color-bg, #0b1020)); + min-height: calc(100dvh - 64px); +} + +.fabric-search:not(.fabric-search--has-results):not(.fabric-search--previewing) .fabric-search__body { + padding-top: clamp(36px, 6vh, 72px); + padding-bottom: clamp(56px, 8vh, 96px); +} + +.fabric-search .hero { + margin-top: 0; +} + +.fabric-search .hero__title { + color: var(--tz-ui-text, #f8fafc); + text-shadow: none; +} + +.fabric-search .hero__subtitle, +.fabric-search .hero__divider, +.fabric-search .category-picker__title, +.fabric-search .db-panel__label, +.fabric-search .limit-slider__label, +.fabric-search .limit-slider__value { + color: var(--tz-ui-text-muted, #cbd5e1); + opacity: 1; +} + +.fabric-search .search-bar, +.fabric-search .category-picker, +.fabric-search .image-preview, +.fabric-search .results-section, +.fabric-search .settings-panel__content, +.fabric-search .db-panel { + background: var(--tz-ui-surface, rgba(15, 23, 42, 0.78)); + border-color: var(--tz-ui-border, rgba(226, 232, 240, 0.2)); + color: var(--tz-ui-text, #f8fafc); + box-shadow: var(--tz-ui-shadow-panel, 0 18px 48px rgba(0, 0, 0, 0.22)); +} + +.fabric-search .search-bar__input, +.fabric-search .search-bar__input::placeholder { + color: var(--tz-ui-text-muted, #cbd5e1); + opacity: 1; +} + +.fabric-search .category-picker__chip { + color: var(--tz-ui-text-soft, #e2e8f0); + background: var(--tz-ui-control-bg, rgba(30, 41, 59, 0.96)); + border-color: var(--tz-ui-border, rgba(226, 232, 240, 0.2)); +} + +.fabric-search .category-picker__chip--active { + color: var(--tz-ui-accent-contrast, #171101); + background: var(--tz-ui-accent, #e0a82e); + border-color: var(--tz-ui-accent, #e0a82e); + box-shadow: 0 12px 26px rgba(224, 168, 46, 0.22); +} + +.fabric-search .category-picker__chip--active .category-picker__chip-label, +.fabric-search .category-picker__chip--active .category-picker__chip-icon, +.fabric-search .category-picker__chip--active .category-picker__chip-check { + color: var(--tz-ui-accent-contrast, #171101); +} + +.fabric-search.fabric-search--previewing .fabric-search__body { + padding-top: clamp(18px, 3vh, 32px); +} + +.fabric-search.fabric-search--previewing .fabric-search__top-bar { + display: flex; + justify-content: flex-end; + width: min(1060px, 100%); + margin-inline: auto; +} + +.fabric-search.fabric-search--previewing .settings-panel__content { + position: static; + transform: none; +} + +.fabric-search .image-preview__pane-title { + color: var(--tz-ui-text, #f8fafc); + background: var(--tz-ui-surface-raised, rgba(23, 31, 51, 0.96)); + border-color: var(--tz-ui-border, rgba(226, 232, 240, 0.2)); +} + +.fabric-search .image-preview__frame-actions .btn { + box-shadow: 0 10px 24px rgba(0, 0, 0, 0.2); +} + +.fabric-search button:focus-visible, +.fabric-search input:focus-visible, +.fabric-search [role="button"]:focus-visible { + outline: 3px solid var(--tz-ui-focus-ring, rgba(224, 168, 46, 0.34)); + outline-offset: 3px; +} + +:root[data-theme="light"] .fabric-search .search-bar, +:root[data-theme="light"] .fabric-search .category-picker, +:root[data-theme="light"] .fabric-search .image-preview, +:root[data-theme="light"] .fabric-search .results-section, +:root[data-theme="light"] .fabric-search .settings-panel__content, +:root[data-theme="light"] .fabric-search .db-panel { + background: var(--tz-ui-surface-raised, rgba(255, 255, 255, 0.96)); + border-color: var(--tz-ui-border, rgba(15, 23, 42, 0.14)); + color: var(--tz-ui-text, #0f172a); +} + +:root[data-theme="light"] .fabric-search .search-bar__input, +:root[data-theme="light"] .fabric-search .search-bar__input::placeholder { + color: var(--tz-ui-text-muted, #475569); +} + +:root[data-theme="light"] .fabric-search .category-picker__chip { + color: var(--tz-ui-text, #0f172a); + background: var(--tz-ui-control-bg, #ffffff); + border-color: var(--tz-ui-border, rgba(15, 23, 42, 0.14)); +} + +@media (max-height: 760px) { + .fabric-search:not(.fabric-search--has-results):not(.fabric-search--previewing) .fabric-search__body { + padding-top: 28px; + } + + .fabric-search .hero__title { + font-size: clamp(40px, 8vw, 72px); + } +} + +/* QA follow-up: Search buttons must remain visible in both themes and while disabled. */ +.fabric-search .hero__search-row .btn--primary, +.fabric-search .image-preview__search-row .btn--primary, +.fabric-search .search-bar__text-row .btn--primary, +.fabric-search .search-bar__image-actions .btn--primary { + background: linear-gradient(135deg, var(--tz-turmeric, #e0a82e), var(--tz-turmeric-soft, #f59e0b)); + border: 1px solid var(--tz-turmeric, #e0a82e); + color: #171101; + opacity: 1; + text-shadow: none; + box-shadow: 0 10px 24px rgba(224, 168, 46, 0.24); +} + +:root[data-theme="light"] .fabric-search .hero__search-row .btn--primary, +:root[data-theme="light"] .fabric-search .image-preview__search-row .btn--primary, +:root[data-theme="light"] .fabric-search .search-bar__text-row .btn--primary, +:root[data-theme="light"] .fabric-search .search-bar__image-actions .btn--primary { + color: #ffffff; +} + +.fabric-search .hero__search-row .btn--primary:disabled, +.fabric-search .image-preview__search-row .btn--primary:disabled, +.fabric-search .search-bar__text-row .btn--primary:disabled, +.fabric-search .search-bar__image-actions .btn--primary:disabled { + opacity: 0.68; + filter: saturate(0.9); + color: #171101; +} + +:root[data-theme="light"] .fabric-search .hero__search-row .btn--primary:disabled, +:root[data-theme="light"] .fabric-search .image-preview__search-row .btn--primary:disabled, +:root[data-theme="light"] .fabric-search .search-bar__text-row .btn--primary:disabled, +:root[data-theme="light"] .fabric-search .search-bar__image-actions .btn--primary:disabled { + color: #ffffff; +} + +/* Senior QA final: Search visual stability, button visibility, settings flow, and zoom toolbar polish. */ +.fabric-search { + --color-muted: var(--tz-ui-text-muted, #cbd5e1); + color: var(--tz-ui-text, #f8fafc); +} + +:root[data-theme="light"] .fabric-search { + --color-muted: #475569; + --color-text: #0f172a; + --color-surface: rgba(255, 255, 255, 0.92); + --color-surface-2: rgba(255, 255, 255, 0.98); + --color-border: rgba(15, 23, 42, 0.12); + --color-border-2: rgba(15, 23, 42, 0.18); + --color-border-gold: rgba(183, 121, 31, 0.36); +} + +.fabric-search__body { + padding-top: clamp(28px, 4vh, 48px); +} + +.fabric-search--has-results { + padding-top: clamp(168px, 22vh, 220px); +} + +.fabric-search .btn, +.fabric-search .pagination__btn, +.fabric-search .settings-panel__toggle, +.fabric-search .category-picker__toggle-all { + min-height: 42px; + border-radius: 999px; + font-weight: 850; +} + +.fabric-search .btn--primary, +.fabric-search .search-bar .btn--primary, +.fabric-search .image-preview__search-row .btn--primary, +.fabric-search .category-picker--compact .btn--primary { + background: linear-gradient(135deg, var(--tz-action-yellow, #e0a82e), var(--tz-action-yellow-hover, #f0c463)); + color: var(--tz-action-yellow-text, #171101); + border: 1px solid rgba(180, 83, 9, 0.36); + box-shadow: 0 12px 28px rgba(224, 168, 46, 0.24); + opacity: 1; + text-shadow: none; +} + +.fabric-search .btn--primary:disabled { + opacity: 0.72; + color: rgba(23, 17, 1, 0.76); + box-shadow: none; +} + +.fabric-search .hero__header { + padding-top: clamp(18px, 4vh, 44px); + padding-bottom: clamp(18px, 3vh, 30px); +} + +.fabric-search .hero__title { + color: var(--tz-ui-text, #f8fafc); + font-size: clamp(34px, 5vw, 60px); + text-shadow: none; +} + +:root[data-theme="light"] .fabric-search .hero__title { + color: #0f172a; +} + +.fabric-search .hero__subtitle, +.fabric-search .hero__eyebrow, +.fabric-search .category-picker__title, +.fabric-search .limit-slider__label, +.fabric-search .limit-slider__value, +.fabric-search .image-preview__pane-title, +.fabric-search .results-section__count-label, +.fabric-search .results-section__filter-prefix, +.fabric-search .pagination__label, +.fabric-search__empty { + color: var(--tz-ui-text-muted, #cbd5e1); + opacity: 1; + text-shadow: none; +} + +:root[data-theme="light"] .fabric-search .hero__subtitle, +:root[data-theme="light"] .fabric-search .hero__eyebrow, +:root[data-theme="light"] .fabric-search .category-picker__title, +:root[data-theme="light"] .fabric-search .limit-slider__label, +:root[data-theme="light"] .fabric-search .limit-slider__value, +:root[data-theme="light"] .fabric-search .image-preview__pane-title, +:root[data-theme="light"] .fabric-search .results-section__count-label, +:root[data-theme="light"] .fabric-search .results-section__filter-prefix, +:root[data-theme="light"] .fabric-search .pagination__label, +:root[data-theme="light"] .fabric-search__empty { + color: #334155; +} + +.fabric-search .settings-panel__toggle { + background: var(--tz-ui-control-bg, rgba(30, 41, 59, 0.94)); + color: var(--tz-ui-text, #f8fafc); + border-color: var(--tz-ui-border, rgba(226, 232, 240, 0.22)); +} + +:root[data-theme="light"] .fabric-search .settings-panel__toggle { + background: #ffffff; + color: #0f172a; + border-color: rgba(15, 23, 42, 0.16); +} + +.fabric-search.fabric-search--previewing .settings-panel__content { + position: static; + margin-top: 10px; + width: min(360px, calc(100vw - 48px)); +} + +.fabric-search.fabric-search--previewing .fabric-search__settings { + width: 100%; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.fabric-search .settings-panel__content { + color: var(--tz-ui-text, #f8fafc); + background: var(--tz-ui-surface-raised, rgba(15, 23, 42, 0.96)); + border: 1px solid var(--tz-ui-border, rgba(226, 232, 240, 0.22)); + box-shadow: var(--tz-ui-shadow-panel, 0 18px 48px rgba(0, 0, 0, 0.28)); +} + +:root[data-theme="light"] .fabric-search .settings-panel__content { + color: #0f172a; + background: #ffffff; + border-color: rgba(15, 23, 42, 0.16); +} + +.fabric-search .category-picker__chip { + min-height: 40px; + border-radius: 999px; + color: var(--tz-ui-text-muted, #cbd5e1); +} + +.fabric-search .category-picker__chip--active, +:root[data-theme="light"] .fabric-search .category-picker__chip--active, +:root[data-theme="dark"] .fabric-search .category-picker__chip--active { + background: linear-gradient(135deg, var(--tz-action-yellow, #e0a82e), var(--tz-action-yellow-hover, #f0c463)); + color: var(--tz-action-yellow-text, #171101); + border-color: var(--tz-action-yellow, #e0a82e); + box-shadow: 0 0 0 2px rgba(224, 168, 46, 0.22), 0 12px 28px rgba(224, 168, 46, 0.16); +} + +.fabric-search .result-grid--full { + align-items: stretch; +} + +.fabric-search .result-card { + display: flex; + flex-direction: column; + min-height: 100%; + border-color: var(--tz-ui-border, rgba(226, 232, 240, 0.18)); +} + +.fabric-search .result-card__thumb { + background: var(--tz-ui-surface-subtle, rgba(15, 23, 42, 0.72)); +} + +.fabric-search .result-card__thumb img { + object-fit: cover; +} + +.fabric-search .result-card__zoom-btn { + opacity: 1; + transform: none; + width: 38px; + height: 38px; + border-radius: 999px; + background: rgba(15, 23, 42, 0.86); + border: 1px solid rgba(226, 232, 240, 0.24); + color: #ffffff; + box-shadow: 0 10px 22px rgba(0, 0, 0, 0.28); +} + +.fabric-search .result-card__name { + color: var(--tz-ui-text, #f8fafc); + min-height: 40px; + display: block; + text-align: left; + background: transparent; + border: 0; + width: 100%; +} + +:root[data-theme="light"] .fabric-search .result-card__name { + color: #0f172a; +} + +.fabric-search .lightbox__stage { + border-radius: 24px; + border: 1px solid rgba(226, 232, 240, 0.22); + box-shadow: 0 24px 70px rgba(0, 0, 0, 0.44); +} + +.fabric-search .lightbox__controls { + left: 50%; + right: auto; + bottom: 18px; + transform: translateX(-50%); + display: inline-flex; + align-items: center; + gap: 8px; + padding: 8px; + border-radius: 999px; + background: rgba(15, 23, 42, 0.88); + border: 1px solid rgba(226, 232, 240, 0.22); + box-shadow: 0 14px 34px rgba(0, 0, 0, 0.34); +} + +.fabric-search .lightbox__control-btn { + width: auto; + min-width: 42px; + height: 40px; + border-radius: 999px; + color: #f8fafc; + background: rgba(30, 41, 59, 0.92); + border: 1px solid rgba(226, 232, 240, 0.26); + font-weight: 900; + line-height: 1; +} + +.fabric-search .lightbox__control-btn:hover:not(:disabled) { + background: rgba(51, 65, 85, 0.98); + transform: translateY(-1px); +} + +.fabric-search .lightbox__control-btn:disabled { + opacity: 0.46; + cursor: not-allowed; +} + +.fabric-search .lightbox__control-btn--reset { + min-width: 96px; +} + +.fabric-search .lightbox__control-btn--close { + background: linear-gradient(135deg, #ef4444, #b91c1c); + color: #ffffff; + border-color: rgba(248, 113, 113, 0.72); +} + +@media (max-width: 760px) { + .fabric-search--has-results { + padding-top: 0; + } + + .search-bar--sticky { + position: sticky; + top: 0; + } + + .fabric-search .lightbox__controls { + max-width: calc(100% - 24px); + overflow-x: auto; + bottom: 12px; + } +} + +/* Senior QA top-up: stabilize Search empty view, primary action, category cards and footer rhythm. */ +.fabric-search { + min-height: calc(100dvh - var(--site-header-height, 64px)); + padding-top: clamp(22px, 4vh, 54px); +} + +.fabric-search .search-page, +.fabric-search .search-shell, +.fabric-search .search-main { + min-height: auto; +} + +.fabric-search .search-form, +.fabric-search .search-row, +.fabric-search .hero-search-row { + align-items: stretch; +} + +.fabric-search .search-button, +.fabric-search .search-btn, +.fabric-search button[type="submit"] { + min-height: 52px; + border-radius: 14px; + border: 1px solid var(--tz-action-yellow, #e0a82e); + background: linear-gradient(135deg, var(--tz-action-yellow, #e0a82e), var(--tz-action-yellow-hover, #f0c463)); + color: var(--tz-action-yellow-text, #171101); + font-weight: 900; + box-shadow: 0 12px 26px rgba(224, 168, 46, 0.22); + opacity: 1; +} + +.fabric-search .search-button:disabled, +.fabric-search .search-btn:disabled, +.fabric-search button[type="submit"]:disabled { + opacity: 0.72; + cursor: not-allowed; + color: rgba(23, 17, 1, 0.78); +} + +.fabric-search .category-picker, +.fabric-search .filter-card, +.fabric-search .filter-panel { + background: var(--tz-ui-surface-raised, rgba(15, 23, 42, 0.86)); + border-color: var(--tz-ui-border, rgba(226, 232, 240, 0.18)); +} + +.fabric-search .category-picker__label, +.fabric-search .filter-label, +.fabric-search .category-picker h3, +.fabric-search .db-control-panel__label, +.fabric-search .settings-panel__label { + color: var(--tz-ui-text, #f8fafc); + opacity: 1; +} + +.fabric-search .category-chip, +.fabric-search .category-button, +.fabric-search .category-picker button { + color: var(--tz-ui-text, #f8fafc); + background: var(--tz-ui-control-bg, rgba(15, 23, 42, 0.78)); + border-color: var(--tz-ui-border, rgba(226, 232, 240, 0.20)); +} + +.fabric-search .category-chip[aria-pressed="true"], +.fabric-search .category-button[aria-pressed="true"], +.fabric-search .category-picker button[aria-pressed="true"] { + color: var(--tz-action-yellow-text, #171101); + background: linear-gradient(135deg, var(--tz-action-yellow, #e0a82e), var(--tz-action-yellow-hover, #f0c463)); + border-color: var(--tz-action-yellow, #e0a82e); +} + +:root[data-theme="light"] .fabric-search .category-picker, +:root[data-theme="light"] .fabric-search .filter-card, +:root[data-theme="light"] .fabric-search .filter-panel { + background: rgba(255, 255, 255, 0.94); + border-color: rgba(15, 23, 42, 0.12); +} + +:root[data-theme="light"] .fabric-search .category-picker__label, +:root[data-theme="light"] .fabric-search .filter-label, +:root[data-theme="light"] .fabric-search .category-picker h3, +:root[data-theme="light"] .fabric-search .db-control-panel__label, +:root[data-theme="light"] .fabric-search .settings-panel__label { + color: #0f172a; +} + +:root[data-theme="light"] .fabric-search .category-chip, +:root[data-theme="light"] .fabric-search .category-button, +:root[data-theme="light"] .fabric-search .category-picker button { + color: #0f172a; + background: rgba(255, 255, 255, 0.96); + border-color: rgba(15, 23, 42, 0.14); +} + +.fabric-search .search-lightbox__toolbar, +.fabric-search .lightbox-toolbar, +.fabric-search .lightbox-controls { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 10px; + padding: 10px; + border-radius: 999px; + background: rgba(8, 11, 20, 0.82); + border: 1px solid rgba(226, 232, 240, 0.18); + box-shadow: 0 18px 44px rgba(0, 0, 0, 0.30); +} + +.fabric-search .search-lightbox__toolbar button, +.fabric-search .lightbox-toolbar button, +.fabric-search .lightbox-controls button { + width: 42px; + height: 42px; + min-width: 42px; + border-radius: 999px; + display: inline-flex; + align-items: center; + justify-content: center; + color: #f8fafc; + background: rgba(15, 23, 42, 0.92); + border: 1px solid rgba(226, 232, 240, 0.20); +} + +.fabric-search .search-lightbox__toolbar button:disabled, +.fabric-search .lightbox-toolbar button:disabled, +.fabric-search .lightbox-controls button:disabled { + opacity: 0.46; + cursor: not-allowed; +} + +@media (max-height: 760px) { + .fabric-search { + padding-top: 18px; + } +} + +/* Senior QA top-up: actual Search lightbox toolbar class names for + / - / Reset / Close polish. */ +.fabric-search .lightbox__controls { + max-width: calc(100vw - 32px); + display: inline-flex; + align-items: center; + justify-content: center; + gap: 10px; + padding: 10px; + border-radius: 999px; + background: rgba(8, 11, 20, 0.86); + border: 1px solid rgba(226, 232, 240, 0.22); + box-shadow: 0 18px 44px rgba(0, 0, 0, 0.34); +} + +.fabric-search .lightbox__control-btn { + width: 42px; + min-width: 42px; + height: 42px; + padding: 0; + border-radius: 999px; + display: inline-flex; + align-items: center; + justify-content: center; + color: #f8fafc; + background: rgba(15, 23, 42, 0.94); + border: 1px solid rgba(226, 232, 240, 0.24); + font-size: 16px; + line-height: 1; +} + +.fabric-search .lightbox__control-btn--reset { + width: auto; + min-width: 98px; + gap: 6px; + padding-inline: 14px; +} + +.fabric-search .lightbox__control-btn:disabled { + opacity: 0.42; + cursor: not-allowed; + transform: none; +} + +.fabric-search .lightbox__control-btn--close { + background: linear-gradient(135deg, #ef4444, #b91c1c); + border-color: rgba(248, 113, 113, 0.72); + color: #ffffff; +} + +@media (max-width: 520px) { + .fabric-search .lightbox__controls { + gap: 6px; + padding: 8px; + } + + .fabric-search .lightbox__control-btn { + width: 38px; + min-width: 38px; + height: 38px; + } + + .fabric-search .lightbox__control-btn--reset { + min-width: 84px; + } +} + + +/* Strict senior QA follow-up: compact search result toolbar, readable apply button, + cleaner filenames, and robust zoom controls at 100–125% browser zoom. */ +.fabric-search.fabric-search--has-results { + padding-top: clamp(142px, 18vh, 184px); +} + +.fabric-search .search-bar--sticky { + min-height: auto; + padding: 10px 0 12px; +} + +.fabric-search .search-bar__filters { + grid-template-columns: minmax(0, 1fr) minmax(210px, 260px); + align-items: center; + gap: 14px; +} + +.fabric-search .search-bar__filters .limit-slider { + align-self: center; + padding: 8px 10px; +} + +.fabric-search .category-picker--compact { + min-height: 0; + padding: 14px 16px; + border-radius: 18px; +} + +.fabric-search .category-picker--compact .category-picker__header { + margin-bottom: 12px; +} + +.fabric-search .category-picker--compact .category-picker__grid { + align-items: center; + gap: 10px; +} + +.fabric-search .category-picker__apply, +.fabric-search .category-picker--compact .category-picker__apply.btn--primary { + min-width: 158px; + min-height: 44px; + padding: 0 18px; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + border-radius: 999px; + border: 1px solid var(--tz-action-yellow, #e0a82e); + background: linear-gradient(135deg, var(--tz-action-yellow, #e0a82e), var(--tz-action-yellow-hover, #f0c463)); + color: var(--tz-action-yellow-text, #171101); + font-weight: 900; + letter-spacing: 0.01em; + box-shadow: 0 12px 28px rgba(224, 168, 46, 0.24); + opacity: 1; + text-shadow: none; +} + +.fabric-search .category-picker__apply span { + color: inherit; + opacity: 1; +} + +.fabric-search .result-card__name { + min-height: 42px; + padding: 12px 14px; + display: block; + width: 100%; + color: var(--tz-ui-text, #f8fafc); + font-family: var(--font-sans, Inter, system-ui, sans-serif); + font-size: 13px; + font-weight: 850; + letter-spacing: 0.005em; + line-height: 1.2; + text-transform: none; + text-align: left; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +:root[data-theme="light"] .fabric-search .result-card__name { + color: #0f172a; +} + +.fabric-search .hero__search-row { + align-items: stretch; +} + +.fabric-search .hero__search-row .btn--primary { + min-width: 116px; +} + +.fabric-search:not(.fabric-search--has-results):not(.fabric-search--previewing) .fabric-search__body { + min-height: calc(100dvh - 132px); +} + +.fabric-search .hero__search-controls { + max-width: 1080px; +} + +.fabric-search .lightbox__stage { + max-width: min(1180px, calc(100vw - 32px)); + max-height: min(840px, calc(100dvh - 32px)); + overflow: hidden; +} + +.fabric-search .lightbox__image { + max-width: calc(100vw - 72px); + max-height: calc(100dvh - 116px); + object-fit: contain; + transform-origin: center center; +} + +.fabric-search .lightbox__controls { + bottom: clamp(12px, 2vh, 20px); + min-height: 54px; + max-width: min(440px, calc(100vw - 28px)); + overflow: visible; +} + +.fabric-search .lightbox__control-btn { + flex: 0 0 auto; +} + +@media (max-width: 980px) { + .fabric-search.fabric-search--has-results { + padding-top: 0; + } + + .fabric-search .search-bar--sticky { + position: sticky; + top: 0; + } + + .fabric-search .search-bar__filters { + grid-template-columns: 1fr; + } +} + +@media (max-height: 760px) and (min-width: 981px) { + .fabric-search.fabric-search--has-results { + padding-top: 134px; + } + + .fabric-search .category-picker--compact { + padding: 12px 14px; + } +} + +/* Senior strict top-up: keep the compact Search toolbar action visible in light theme too. */ +.fabric-search .category-picker--compact .category-picker__apply.btn.btn--primary, +:root[data-theme="light"] .fabric-search .category-picker--compact .category-picker__apply.btn.btn--primary, +:root[data-theme="dark"] .fabric-search .category-picker--compact .category-picker__apply.btn.btn--primary { + visibility: visible; + min-width: 166px; + min-height: 46px; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + background: linear-gradient(135deg, #e0a82e 0%, #facc15 100%); + color: #171101; + border: 1px solid rgba(180, 83, 9, 0.38); + box-shadow: 0 14px 30px rgba(224, 168, 46, 0.28); + opacity: 1; + text-shadow: none; +} + +.fabric-search .category-picker--compact .category-picker__apply.btn.btn--primary span, +:root[data-theme="light"] .fabric-search .category-picker--compact .category-picker__apply.btn.btn--primary span, +:root[data-theme="dark"] .fabric-search .category-picker--compact .category-picker__apply.btn.btn--primary span { + display: inline-flex; + color: #171101; + opacity: 1; + visibility: visible; + font-weight: 900; + text-shadow: none; +} + +.fabric-search .category-picker--compact .category-picker__apply.btn.btn--primary:hover, +:root[data-theme="light"] .fabric-search .category-picker--compact .category-picker__apply.btn.btn--primary:hover, +:root[data-theme="dark"] .fabric-search .category-picker--compact .category-picker__apply.btn.btn--primary:hover { + background: linear-gradient(135deg, #f0c463 0%, #facc15 100%); + transform: translateY(-1px); +} + +.fabric-search .category-picker--compact .category-picker__apply.btn.btn--primary:focus-visible, +:root[data-theme="light"] .fabric-search .category-picker--compact .category-picker__apply.btn.btn--primary:focus-visible, +:root[data-theme="dark"] .fabric-search .category-picker--compact .category-picker__apply.btn.btn--primary:focus-visible { + outline: 3px solid rgba(224, 168, 46, 0.36); + outline-offset: 3px; +} + +:root[data-theme="light"] .fabric-search .category-picker--compact, +:root[data-theme="light"] .fabric-search .search-bar--sticky { + color: #0f172a; +} + +/* Final senior QA top-up: remove the light-theme Settings wrapper box, compact the + result toolbar, and keep Apply Filter visible only when it actually exists. */ +.fabric-search .fabric-search__settings, +:root[data-theme="light"] .fabric-search .fabric-search__settings, +:root[data-theme="dark"] .fabric-search .fabric-search__settings, +.fabric-search .settings-panel, +:root[data-theme="light"] .fabric-search .settings-panel, +:root[data-theme="dark"] .fabric-search .settings-panel { + background: transparent; + border: 0; + box-shadow: none; + padding: 0; + margin: 0; + border-radius: 0; + backdrop-filter: none; + -webkit-backdrop-filter: none; + overflow: visible; +} + +.fabric-search .settings-panel__toggle, +:root[data-theme="light"] .fabric-search .settings-panel__toggle, +:root[data-theme="dark"] .fabric-search .settings-panel__toggle { + min-height: 48px; + padding: 0 22px; + border-radius: 999px; + background: var(--tz-ui-surface-raised, rgba(15, 23, 42, 0.92)); + border: 1px solid var(--tz-ui-border, rgba(226, 232, 240, 0.22)); + box-shadow: 0 14px 34px rgba(15, 23, 42, 0.14); + color: var(--tz-ui-text, #f8fafc); + overflow: hidden; + outline-offset: 3px; +} + +:root[data-theme="light"] .fabric-search .settings-panel__toggle { + background: rgba(255, 255, 255, 0.92); + border-color: rgba(15, 23, 42, 0.14); + color: #0f172a; + box-shadow: 0 14px 34px rgba(15, 23, 42, 0.10); +} + +.fabric-search .settings-panel__toggle::before, +.fabric-search .settings-panel__toggle::after, +.fabric-search .settings-panel::before, +.fabric-search .settings-panel::after, +.fabric-search .fabric-search__settings::before, +.fabric-search .fabric-search__settings::after { + content: none; + display: none; +} + +.fabric-search .search-bar--sticky { + padding: 10px clamp(10px, 1.2vw, 18px) 12px; +} + +.fabric-search .search-bar__inner, +.fabric-search .search-bar__filters { + max-width: min(1340px, calc(100vw - 44px)); + padding-inline: 0; +} + +.fabric-search .search-bar__filters { + grid-template-columns: minmax(0, 1fr) minmax(184px, 236px); + gap: clamp(10px, 1.2vw, 16px); + align-items: stretch; +} + +.fabric-search .category-picker--compact { + padding: 14px 16px; + min-height: 126px; + display: flex; + flex-direction: column; + justify-content: center; +} + +.fabric-search .category-picker--compact .category-picker__header { + margin-bottom: 10px; +} + +.fabric-search .category-picker--compact .category-picker__grid { + display: flex; + align-items: center; + gap: 9px; + row-gap: 9px; +} + +.fabric-search .category-picker--compact .category-picker__chip { + min-height: 42px; + padding: 0 15px; + border-radius: 999px; +} + +.fabric-search .category-picker--compact .category-picker__apply.btn.btn--primary { + min-width: 150px; + min-height: 42px; + flex: 0 0 auto; +} + +.fabric-search .category-picker__apply-label, +.fabric-search .category-picker__apply-arrow { + display: inline-flex; + align-items: center; + color: inherit; + opacity: 1; + visibility: visible; +} + +.fabric-search .search-bar__filters .limit-slider { + min-height: 126px; + display: flex; + align-items: center; + gap: 10px; + padding: 14px 8px; +} + +.fabric-search .search-bar__image-row { + flex: 1 1 auto; +} + +.fabric-search .search-bar__image-actions .btn, +.fabric-search .search-bar__inner > .btn { + border-radius: 999px; + min-height: 44px; + font-weight: 850; + white-space: nowrap; +} + +.fabric-search .result-card__name { + min-height: 48px; + padding: 12px 14px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-transform: none; +} + +.fabric-search .results-section { + margin-top: clamp(14px, 2vh, 24px); +} + +.fabric-search .lightbox__controls { + gap: 8px; + padding: 8px; + min-height: 52px; +} + +.fabric-search .lightbox__control-btn { + width: 42px; + min-width: 42px; + height: 42px; +} + +.fabric-search .lightbox__control-btn--reset { + min-width: 96px; +} + +@media (max-width: 980px) { + .fabric-search .search-bar__filters { + grid-template-columns: 1fr; + } + + .fabric-search .search-bar__filters .limit-slider, + .fabric-search .category-picker--compact { + min-height: auto; + } +} + +/* Final micro top-up: center Search CTA text and remove light-theme Settings wrapper artefact. */ +.fabric-search .btn, +.fabric-search .btn--primary, +.fabric-search .settings-panel__toggle { + justify-content: center; + text-align: center; +} + +.fabric-search .hero__search-row > .btn--primary, +.fabric-search .search-bar__text-row > .btn--primary, +.fabric-search .image-preview__search-row > .btn--primary { + min-width: 136px; + padding-inline: 24px; + line-height: 1; + display: inline-flex; + align-items: center; + justify-content: center; + text-align: center; +} + +.fabric-search .hero__search-row > .btn--primary { + flex: 0 0 136px; +} + +.fabric-search .settings-panel, +.fabric-search .fabric-search__settings, +.fabric-search .fabric-search__top-bar { + background: transparent; + border: 0; + box-shadow: none; + backdrop-filter: none; + -webkit-backdrop-filter: none; +} + +.fabric-search .settings-panel { + display: inline-flex; + width: max-content; + max-width: max-content; + overflow: visible; + contain: layout; +} + +.fabric-search .settings-panel__toggle { + width: auto; + max-width: max-content; + background-clip: padding-box; + border-radius: 999px; + overflow: hidden; + backdrop-filter: none; + -webkit-backdrop-filter: none; + clip-path: inset(0 round 999px); +} + +:root[data-theme="light"] .fabric-search .settings-panel__toggle { + background: rgba(255, 255, 255, 0.96); + border-color: rgba(15, 23, 42, 0.16); + box-shadow: 0 12px 28px rgba(15, 23, 42, 0.10); +} + +/* v39: keep the stacked original/cropped preview usable on tablet and mobile + viewports. The document body is intentionally contained by the legacy shell, + so the preview route owns its vertical scroll at the same breakpoint where + the two preview panes become a single column. Desktop preview geometry and + all crop/search behavior remain unchanged. */ +@media (max-width: 900px) { + .fabric-search.fabric-search--previewing { + box-sizing: border-box; + height: calc(100dvh - var(--site-header-height, 64px)); + min-height: 0; + overflow-x: hidden; + overflow-y: auto; + overscroll-behavior-y: contain; + -webkit-overflow-scrolling: touch; + } + + .fabric-search.fabric-search--previewing .fabric-search__body { + box-sizing: border-box; + width: 100%; + max-width: 100%; + min-height: max-content; + padding-inline: clamp(12px, 3vw, 22px); + overflow: visible; + } + + .fabric-search.fabric-search--previewing .image-preview { + width: 100%; + min-height: max-content; + overflow: visible; + } +} + +/* v40: keep the pre-search image actions visible whenever the preview panes + stack. The older desktop rail used a two-column grid that could leave Clear + and Recrop outside the visible mobile flow. At the same breakpoint used by + the v39 scroll container, place the actions directly below the original + image. Search/crop handlers and desktop geometry are unchanged. */ +@media (max-width: 900px) { + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__frame { + height: auto; + max-height: none; + } + + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions { + display: flex; + flex-direction: column; + align-items: stretch; + flex: 0 0 auto; + min-height: 0; + height: auto; + overflow: visible; + gap: 12px; + padding: 12px; + } + + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__img { + flex: 0 0 auto; + width: 100%; + height: auto; + max-height: min(58dvh, 460px); + object-fit: contain; + } + + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail { + position: static; + inset: auto; + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + align-items: stretch; + justify-content: stretch; + width: 100%; + min-width: 0; + margin: 0; + padding: 0; + gap: 10px; + transform: none; + } + + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__action-rail + .btn { + width: 100%; + min-width: 0; + margin: 0; + justify-content: center; + } + + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:first-child { + top: 0; + } +} + +@media (max-width: 360px) { + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail { + grid-template-columns: 1fr; + } +} + + +/* v47: use the available desktop canvas instead of leaving large side gutters. + The Search body, preview workspace, sticky controls and results share one + responsive content width so inputs, sliders, categories, panes and cards grow + together rather than stretching only their outer borders. Mobile/tablet + breakpoints remain unchanged. */ +@media (min-width: 1101px) { + .fabric-search:not(.fabric-search--has-results) .fabric-search__body, + .fabric-search.fabric-search--previewing .fabric-search__body { + box-sizing: border-box; + width: min(1600px, calc(100vw - 64px)); + max-width: min(1600px, calc(100vw - 64px)); + padding-inline: clamp(24px, 2.5vw, 44px); + } + + .fabric-search .hero__search-controls { + width: 100%; + max-width: none; + } + + .fabric-search .hero__search-row, + .fabric-search .hero__upload-btn, + .fabric-search .hero__categories, + .fabric-search .hero__categories .category-picker, + .fabric-search .image-preview, + .fabric-search .image-preview__options, + .fabric-search .image-preview__search-row { + width: 100%; + } + + .fabric-search + :is( + .hero__categories, + .image-preview__options + ) + .category-picker__grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: clamp(10px, 1vw, 16px); + } + + .fabric-search + :is( + .hero__categories, + .image-preview__options + ) + .category-picker__chip { + width: 100%; + min-width: 0; + justify-content: center; + } + + .fabric-search.fabric-search--previewing .image-preview { + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: clamp(24px, 2.2vw, 38px); + } + + .fabric-search .search-bar__inner, + .fabric-search .search-bar__filters, + .fabric-search .results-section { + box-sizing: border-box; + width: min(1600px, calc(100vw - 64px)); + max-width: min(1600px, calc(100vw - 64px)); + margin-inline: auto; + } +} diff --git a/frontend/src/assets/styles/GlobalUiConsistency.css b/frontend/src/assets/styles/GlobalUiConsistency.css new file mode 100644 index 0000000..7a529dd --- /dev/null +++ b/frontend/src/assets/styles/GlobalUiConsistency.css @@ -0,0 +1,111 @@ +/* Final cross-route presentation guard. + This file is intentionally imported last: it normalizes shared layout and + error semantics without changing route logic or request handling. */ + +:root { + --tz-error-surface: #dc2626; + --tz-error-border: #991b1b; + --tz-error-text: #ffffff; +} + +/* Header, content and footer remain one stable flex column. Home uses its + natural content height, so large screens show the footer without a redundant + scrollbar while shorter screens can still scroll normally. */ +.site-header, +.site-footer { + flex: 0 0 auto; +} + +.main-content { + flex: 1 1 auto; +} + +/* The filename label is supporting copy, not the primary page heading. */ +.upload-page .name-label { + margin: 0; + color: var(--tz-muted, #94a3b8) !important; + font-size: clamp(16px, 1.35vw, 20px) !important; + font-weight: 600 !important; + line-height: 1.3; + letter-spacing: 0.01em; +} + +:root[data-theme="light"] .upload-page .name-label { + color: #64748b !important; +} + +.upload-page .name-label__optional { + color: inherit; + font-size: 0.9em; + font-style: italic; + font-weight: 500; +} + +/* All user-facing failures use the same strong red surface and white text. + The role selector covers Reader, Search, Chat, Upload and Composer errors; + named selectors cover non-ARIA legacy error containers. Explicit text-fill + values prevent inherited gradient/text-fill rules from rendering dark text + inside an otherwise-correct red error surface. */ +:root [role="alert"]:not(.app-error), +:root .notification.error, +:root .fabric-search__error, +:root .grid-error, +:root .db-panel__notification--error, +:root .description-wrapper .description-box.error, +:root .analysis-popup__card, +:root .app-error__card { + box-sizing: border-box; + padding: 12px 16px; + background: var(--tz-error-surface) !important; + background-image: none !important; + color: var(--tz-error-text) !important; + -webkit-text-fill-color: var(--tz-error-text) !important; + border: 1px solid var(--tz-error-border) !important; + border-radius: 12px; + box-shadow: 0 10px 24px rgba(220, 38, 38, 0.24) !important; + text-shadow: none !important; + mix-blend-mode: normal !important; + filter: none !important; + opacity: 1 !important; + font-weight: 600; +} + +:root [role="alert"]:not(.app-error) *, +:root .notification.error *, +:root .fabric-search__error *, +:root .grid-error *, +:root .db-panel__notification--error *, +:root .description-wrapper .description-box.error *, +:root .description-wrapper .error-text, +:root .analysis-popup__card *, +:root .app-error__card * { + color: var(--tz-error-text) !important; + -webkit-text-fill-color: var(--tz-error-text) !important; + background-image: none !important; + -webkit-background-clip: border-box !important; + background-clip: border-box !important; + text-shadow: none !important; + mix-blend-mode: normal !important; + filter: none !important; + opacity: 1 !important; +} + +/* Keep the Analysis error acknowledgement clearly actionable while remaining + inside the shared red/white error language. */ +:root .analysis-popup__button { + background: var(--tz-error-border) !important; + color: var(--tz-error-text) !important; + border-color: rgba(255, 255, 255, 0.4) !important; +} + +/* AppErrorBoundary's action should not reintroduce an unrelated amber surface. */ +:root .app-error__actions button { + background: var(--tz-error-border) !important; + color: var(--tz-error-text) !important; +} + +@media (max-width: 720px) { + .upload-page .name-label { + font-size: 16px !important; + } +} diff --git a/frontend/src/assets/styles/Home.css b/frontend/src/assets/styles/Home.css index 6e44cbc..b14b81d 100644 --- a/frontend/src/assets/styles/Home.css +++ b/frontend/src/assets/styles/Home.css @@ -1,6 +1,7 @@ .homeV2 { position: relative; - min-height: 85vh; + width: 100%; + min-height: 0; display: flex; justify-content: center; color: var(--text-color); @@ -10,8 +11,9 @@ } .homeV2__container { + width: min(100%, 1280px); max-width: 80vw; - padding: 36px 20px 56px; + padding: clamp(20px, 3vh, 36px) 20px clamp(24px, 4vh, 40px); } .homeV2__hero { diff --git a/frontend/src/assets/styles/HomeViewportNavigationFix.css b/frontend/src/assets/styles/HomeViewportNavigationFix.css new file mode 100644 index 0000000..a0bbddd --- /dev/null +++ b/frontend/src/assets/styles/HomeViewportNavigationFix.css @@ -0,0 +1,342 @@ +/* Home viewport + responsive navigation guard. + * + * Loaded after the legacy makeover layers so the Home route uses the available + * application shell height instead of stacking another viewport minimum below + * the header. Desktop Home content is vertically balanced inside that space; + * short desktop viewports receive a compact, readable layout so the footer is + * visible without a redundant page scrollbar. On mobile, the navigation menu + * is viewport-fixed and remains usable even after the document has scrolled. + */ + +.app-wrapper--home .main-content { + display: flex; + min-height: 0; +} + +.app-wrapper--home .homeV2 { + flex: 1 1 auto; + min-height: 0; +} + +@media (min-width: 901px) { + .app-wrapper--home .homeV2__container { + display: flex; + flex-direction: column; + justify-content: center; + } +} + +/* Compact Home only when desktop viewport height is constrained. The regular + spacious desktop presentation remains unchanged on taller displays. */ +@media (min-width: 901px) and (max-height: 960px) { + .app-wrapper--home .homeV2__container { + padding-block: 14px 16px; + } + + .app-wrapper--home .homeV2__hero { + margin: 0 0 12px; + } + + .app-wrapper--home .homeV2__title { + font-size: 38px; + line-height: 1.1; + } + + .app-wrapper--home .homeV2__titleAccent { + margin-top: 3px; + } + + .app-wrapper--home .homeV2__subtitle { + max-width: 780px; + margin-top: 8px; + font-size: 15px; + line-height: 1.4; + } + + .app-wrapper--home .homeV2__features { + gap: 14px; + margin-top: 14px; + } + + .app-wrapper--home .featureCard { + padding: 14px 15px; + } + + .app-wrapper--home .featureCard__icon { + width: 34px; + height: 34px; + margin-bottom: 5px; + } + + .app-wrapper--home .featureCard__title { + margin: 3px 0 4px; + } + + .app-wrapper--home .featureCard__text { + font-size: 13.5px; + line-height: 1.4; + } + + .app-wrapper--home .homeV2__steps { + margin-top: 16px; + padding: 15px; + } + + .app-wrapper--home .steps__heading { + margin-bottom: 9px; + font-size: 17px; + } + + .app-wrapper--home .steps__grid { + gap: 11px; + } + + .app-wrapper--home .stepCard { + padding: 12px; + } + + .app-wrapper--home .stepCard__badge { + width: 28px; + height: 28px; + } + + .app-wrapper--home .stepCard__title { + margin: 7px 0 3px; + font-size: 14px; + } + + .app-wrapper--home .stepCard__text { + margin-bottom: 6px; + font-size: 12.5px; + line-height: 1.35; + } + + .app-wrapper--home .stepCard__link { + font-size: 12.5px; + } +} + +/* Extra tightening for common 768px-tall laptop displays. */ +@media (min-width: 901px) and (max-height: 820px) { + .app-wrapper--home .homeV2__container { + padding-block: 10px 12px; + } + + .app-wrapper--home .homeV2__hero { + margin-bottom: 7px; + } + + .app-wrapper--home .homeV2__title { + font-size: 32px; + line-height: 1.06; + } + + .app-wrapper--home .homeV2__titleAccent { + margin-top: 1px; + } + + .app-wrapper--home .homeV2__subtitle { + max-width: 740px; + margin-top: 5px; + font-size: 13.5px; + line-height: 1.3; + } + + .app-wrapper--home .homeV2__features { + gap: 10px; + margin-top: 8px; + } + + .app-wrapper--home .featureCard { + padding: 9px 12px; + } + + .app-wrapper--home .featureCard__icon { + width: 28px; + height: 28px; + margin-bottom: 2px; + } + + .app-wrapper--home .featureCard__title { + margin: 1px 0 2px; + font-size: 14px; + } + + .app-wrapper--home .featureCard__text { + font-size: 12.5px; + line-height: 1.3; + } + + .app-wrapper--home .homeV2__steps { + margin-top: 9px; + padding: 9px 11px; + } + + .app-wrapper--home .steps__heading { + margin-bottom: 6px; + font-size: 15px; + } + + .app-wrapper--home .steps__grid { + gap: 8px; + } + + .app-wrapper--home .stepCard { + padding: 8px 10px; + } + + .app-wrapper--home .stepCard__badge { + width: 23px; + height: 23px; + font-size: 12px; + } + + .app-wrapper--home .stepCard__title { + margin: 4px 0 1px; + font-size: 13px; + } + + .app-wrapper--home .stepCard__text { + margin-bottom: 3px; + font-size: 11.5px; + line-height: 1.25; + } + + .app-wrapper--home .stepCard__link { + font-size: 11.5px; + } +} + +@media (max-width: 768px) { + body { + contain: none; + } + + .site-header { + position: relative; + z-index: 1000; + overflow: visible; + backdrop-filter: none; + -webkit-backdrop-filter: none; + } + + .site-header .mobile-nav-wrapper { + position: static; + } + + .site-header .header-nav ul { + position: fixed; + inset: 0; + z-index: 1001; + box-sizing: border-box; + width: 100vw; + min-height: 100dvh; + height: 100dvh; + max-height: 100dvh; + overflow-y: auto; + overscroll-behavior: contain; + } + + .site-header .hamburger { + z-index: 1002; + } +} + +/* Zoomed-out Home hierarchy. + * + * Browser zoom-out exposes a much larger effective CSS viewport. Keep the + * entire Home content stack together near the top while preserving its centred + * horizontal alignment. Common 100% desktop and mobile viewports stay on the + * existing v30 layout because this guard only activates beyond standard QHD + * dimensions. + */ +@media (min-width: 2600px) and (min-height: 1400px) { + .app-wrapper--home .homeV2__container { + justify-content: flex-start; + padding-block-start: clamp(32px, 3.5vh, 72px); + padding-block-end: 0; + } + + .app-wrapper--home .homeV2__hero { + flex: 0 0 auto; + margin: 0 0 clamp(24px, 2vh, 40px); + } + + .app-wrapper--home .homeV2__features { + flex: 0 0 auto; + margin-top: 0; + } + + .app-wrapper--home .homeV2__steps { + flex: 0 0 auto; + margin-top: clamp(20px, 1.75vh, 32px); + margin-bottom: 0; + } +} + + +/* v44: heavy browser zoom-out should keep the Home/Analysis overview directly + below the navbar. Also render the lower "How to Use FabricAI" panel with the + same visual weight as the feature cards instead of a faint placeholder-like + surface. Normal desktop and mobile layouts remain unchanged. */ +@media (min-width: 2600px) and (min-height: 1400px) { + .app-wrapper--home .homeV2__container { + justify-content: flex-start; + padding-block-start: 10px; + } + + .app-wrapper--home .homeV2__hero { + margin-bottom: 14px; + } + + .app-wrapper--home .homeV2__features { + margin-top: 0; + } + + .app-wrapper--home .homeV2__steps { + content-visibility: visible; + margin-top: 14px; + background: var(--tz-panel, #11182a); + border-color: rgba(148, 163, 184, 0.38); + box-shadow: + 0 12px 30px rgba(2, 6, 23, 0.24), + inset 0 1px 0 rgba(255, 255, 255, 0.04); + } + + .app-wrapper--home .homeV2__steps .stepCard { + background: var(--tz-panel-2, #182138); + border-color: rgba(148, 163, 184, 0.34); + } + + :root[data-theme="light"] .app-wrapper--home .homeV2__steps { + background: var(--tz-upload-panel-bg, #ffffff); + border-color: rgba(15, 23, 42, 0.16); + box-shadow: 0 10px 26px rgba(15, 23, 42, 0.1); + } + + :root[data-theme="light"] .app-wrapper--home .homeV2__steps .stepCard { + background: #f9fbff; + border-color: rgba(15, 23, 42, 0.14); + } +} + +/* v46: Home should begin directly below the shared navbar at every zoom level. + The previous desktop rule vertically centred the full overview inside the + available shell, which reintroduced a large empty band above the heading at + ordinary zoom. Keep the same responsive sizing, but anchor the content stack + to the top on desktop and reduce only the leading page inset on smaller views. */ +.app-wrapper--home .homeV2__container { + padding-block-start: 10px; +} + +@media (min-width: 901px) { + .app-wrapper--home .homeV2__container { + justify-content: flex-start; + } +} + +@media (max-width: 900px) { + .app-wrapper--home .homeV2__container { + padding-block-start: 12px; + } +} diff --git a/frontend/src/assets/styles/ImageDescription.css b/frontend/src/assets/styles/ImageDescription.css index e37a5ed..9b5de0b 100644 --- a/frontend/src/assets/styles/ImageDescription.css +++ b/frontend/src/assets/styles/ImageDescription.css @@ -692,3 +692,454 @@ :root[data-theme="dark"] .analysis-page .analysis-popup__content p { color: #cbd5e1; } + +/* Senior QA final: Analysis page vertical rhythm and header visibility. */ +.analysis-page.home-container { + padding-top: clamp(18px, 3vh, 36px); + padding-bottom: clamp(36px, 5vh, 64px); + min-height: calc(100dvh - 64px); +} + +.analysis-page .animated-header { + min-height: 54px; + margin-bottom: clamp(10px, 2vh, 18px); +} + +.analysis-page .animated-header h1 { + color: var(--tz-ui-text, #f8fafc); + border-bottom-color: var(--tz-action-yellow, #e0a82e); + text-shadow: none; +} + +:root[data-theme="light"] .analysis-page .animated-header h1 { + color: #0f172a; +} + +.analysis-page .grid { + align-items: start; + gap: clamp(18px, 3vw, 30px); +} + +@media (max-height: 760px) { + .analysis-page.home-container { + padding-top: 12px; + } + + .analysis-page .animated-header { + margin-bottom: 8px; + } +} + +/* Senior QA top-up: Analysis layout must stay balanced at 1366x768 and 125% zoom. */ +.analysis-page.home-container { + overflow-x: hidden; +} + +.analysis-page .top-texts { + color: var(--tz-ui-text, #f8fafc); + font-weight: 850; +} + +:root[data-theme="dark"] .analysis-page .sample-text, +:root[data-theme="dark"] .analysis-page .top-texts { + color: #f8fafc; + opacity: 1; +} + +.analysis-page .sample-text { + color: var(--tz-action-yellow, #e0a82e); + text-shadow: none; +} + +.analysis-page .result-wrapper.analysis-grid { + width: min(1120px, calc(100vw - 64px)); +} + +.analysis-page.drawer-open .result-wrapper.analysis-grid { + width: min(920px, calc(100vw - 340px)); +} + +.analysis-page .drawer-panel { + width: 270px; + padding: 18px 14px 24px; +} + +@media (max-width: 1180px) { + .analysis-page.drawer-open .result-wrapper.analysis-grid { + width: min(860px, calc(100vw - 318px)); + gap: 18px; + } +} + +@media (max-width: 980px) { + .analysis-page.drawer-open .result-wrapper.analysis-grid, + .analysis-page .result-wrapper.analysis-grid { + width: min(720px, calc(100vw - 32px)); + } +} + + +/* Strict senior QA follow-up: keep Analysis balanced when the sample drawer is open at laptop height/zoom. */ +.analysis-page.home-container { + padding-top: clamp(14px, 2.4vh, 28px); + padding-bottom: clamp(28px, 4vh, 48px); +} + +.analysis-page .top-texts { + margin-bottom: clamp(12px, 2vh, 18px); +} + +.analysis-page.drawer-open .result-wrapper.analysis-grid { + width: min(900px, calc(100vw - 340px)); + gap: clamp(18px, 2vw, 26px); +} + +.analysis-page .drawer-panel { + width: min(280px, 22vw); + background: var(--tz-ui-surface-raised, #f8fafc); +} + +:root[data-theme="dark"] .analysis-page .drawer-panel { + background: #f8fafc; + color: #0f172a; +} + +.analysis-page .drawer-panel .sample-gallery h2 { + position: sticky; + top: 0; + z-index: 2; + padding: 4px 0 12px; + margin-bottom: 12px; + background: inherit; +} + +@media (max-height: 760px) and (min-width: 981px) { + .analysis-page .dropzone.big-drop, + .analysis-page .preview-image, + .analysis-page .description-wrapper .description-box { + height: clamp(240px, 38dvh, 360px); + min-height: clamp(240px, 38dvh, 360px); + max-height: clamp(240px, 38dvh, 360px); + } + + .analysis-page .drawer-panel .sample-gallery .sample-card { + height: 172px; + } +} + +/* Senior strict top-up: the Analysis sample drawer must keep the same dark, premium surface in both themes. */ +.analysis-page .drawer-panel, +:root[data-theme="light"] .analysis-page .drawer-panel, +:root[data-theme="dark"] .analysis-page .drawer-panel { + background: + radial-gradient(circle at 50% 0%, rgba(224, 168, 46, 0.10), transparent 34%), + linear-gradient(180deg, #0b1020 0%, #0a0f1e 48%, #070b16 100%); + color: #f8fafc; + border-left: 1px solid rgba(226, 232, 240, 0.18); + box-shadow: -24px 0 54px rgba(0, 0, 0, 0.38); +} + +.analysis-page .drawer-panel .sample-gallery, +:root[data-theme="light"] .analysis-page .drawer-panel .sample-gallery, +:root[data-theme="dark"] .analysis-page .drawer-panel .sample-gallery { + color: #f8fafc; +} + +.analysis-page .drawer-panel .sample-gallery h2, +:root[data-theme="light"] .analysis-page .drawer-panel .sample-gallery h2, +:root[data-theme="dark"] .analysis-page .drawer-panel .sample-gallery h2 { + color: #f8fafc; + background: + linear-gradient(180deg, rgba(11, 16, 32, 0.98), rgba(11, 16, 32, 0.88)); + text-shadow: 0 2px 14px rgba(0, 0, 0, 0.42); +} + +.analysis-page .drawer-panel .sample-gallery .sample-card, +:root[data-theme="light"] .analysis-page .drawer-panel .sample-gallery .sample-card, +:root[data-theme="dark"] .analysis-page .drawer-panel .sample-gallery .sample-card { + background: #111827; + border-color: rgba(226, 232, 240, 0.20); + box-shadow: 0 16px 34px rgba(0, 0, 0, 0.34); +} + +.analysis-page .drawer-panel .sample-gallery .sample-img, +:root[data-theme="light"] .analysis-page .drawer-panel .sample-gallery .sample-img, +:root[data-theme="dark"] .analysis-page .drawer-panel .sample-gallery .sample-img { + background: #0f172a; +} + +.analysis-page .drawer-panel::-webkit-scrollbar-thumb, +:root[data-theme="light"] .analysis-page .drawer-panel::-webkit-scrollbar-thumb, +:root[data-theme="dark"] .analysis-page .drawer-panel::-webkit-scrollbar-thumb { + background: rgba(226, 232, 240, 0.36); +} + +.analysis-page .drawer-panel::-webkit-scrollbar-track, +:root[data-theme="light"] .analysis-page .drawer-panel::-webkit-scrollbar-track, +:root[data-theme="dark"] .analysis-page .drawer-panel::-webkit-scrollbar-track { + background: rgba(15, 23, 42, 0.44); +} + +/* Final senior QA top-up: integrate the Analysis sample drawer with the page + instead of making it feel pasted on, and protect laptop/125% zoom layouts. */ +.analysis-page.drawer-open .result-wrapper.analysis-grid, +.analysis-page.drawer-open .result-wrapper.grid { + width: min(960px, calc(100vw - clamp(294px, 24vw, 336px))); + margin-left: clamp(18px, 3vw, 48px); + margin-right: auto; + gap: clamp(16px, 2vw, 24px); +} + +.analysis-page .drawer-panel, +:root[data-theme="light"] .analysis-page .drawer-panel, +:root[data-theme="dark"] .analysis-page .drawer-panel { + width: clamp(248px, 19vw, 272px); + padding: 12px 10px 18px; + background: + radial-gradient(circle at 50% 0%, rgba(224, 168, 46, 0.09), transparent 32%), + linear-gradient(180deg, #111827 0%, #0b1020 48%, #080d18 100%); + border-left: 1px solid rgba(226, 232, 240, 0.16); + box-shadow: -14px 0 34px rgba(15, 23, 42, 0.20); +} + +:root[data-theme="light"] .analysis-page .drawer-panel { + box-shadow: -12px 0 32px rgba(15, 23, 42, 0.18); +} + +.analysis-page .drawer-panel .sample-gallery h2, +:root[data-theme="light"] .analysis-page .drawer-panel .sample-gallery h2, +:root[data-theme="dark"] .analysis-page .drawer-panel .sample-gallery h2 { + margin: 0 0 12px; + padding: 12px 8px 14px; + border-radius: 0 0 16px 16px; + background: linear-gradient(180deg, rgba(15, 23, 42, 0.96), rgba(15, 23, 42, 0.86)); + color: #f8fafc; + font-size: clamp(19px, 1.8vw, 24px); + line-height: 1.15; + letter-spacing: -0.025em; + text-shadow: none; +} + +.analysis-page .drawer-panel .sample-gallery .sample-grid, +:root[data-theme="light"] .analysis-page .drawer-panel .sample-gallery .sample-grid, +:root[data-theme="dark"] .analysis-page .drawer-panel .sample-gallery .sample-grid { + gap: 10px; +} + +.analysis-page .drawer-panel .sample-gallery .sample-card, +:root[data-theme="light"] .analysis-page .drawer-panel .sample-gallery .sample-card, +:root[data-theme="dark"] .analysis-page .drawer-panel .sample-gallery .sample-card { + height: clamp(138px, 20dvh, 166px); + border-radius: 14px; + border-color: rgba(226, 232, 240, 0.18); + box-shadow: 0 10px 24px rgba(0, 0, 0, 0.26); +} + +.analysis-page .drawer-panel .sample-gallery .sample-img { + border-radius: 14px; +} + +.analysis-page .drawer-panel .sample-gallery .sample-card__select { + bottom: 10px; + min-height: 32px; + min-width: 84px; + padding: 0 14px; + font-size: 12px; +} + +.analysis-page .dropzone.big-drop, +.analysis-page .description-wrapper .description-box, +.analysis-page .preview-image { + box-shadow: 0 18px 42px rgba(15, 23, 42, 0.14); +} + +:root[data-theme="light"] .analysis-page .dropzone.big-drop { + background: rgba(255, 255, 255, 0.82); + border-color: rgba(15, 23, 42, 0.10); + box-shadow: 0 18px 42px rgba(15, 23, 42, 0.10); +} + +.analysis-page .top-texts { + margin-bottom: clamp(12px, 1.8vh, 18px); +} + +@media (max-height: 760px) and (min-width: 981px) { + .analysis-page .drawer-panel .sample-gallery .sample-card { + height: 132px; + } + + .analysis-page .drawer-panel .sample-gallery h2 { + font-size: 19px; + padding-block: 10px; + } +} + +/* Final micro top-up: let the Analysis sample drawer follow the active theme. + Previous strict QA patches intentionally forced a dark drawer; this restores + normal light/dark behaviour while keeping both themes readable. */ +:root[data-theme="light"] .analysis-page .drawer-panel { + background: + radial-gradient(circle at 50% 0%, rgba(224, 168, 46, 0.10), transparent 34%), + linear-gradient(180deg, rgba(255, 255, 255, 0.98) 0%, rgba(248, 250, 252, 0.98) 52%, rgba(239, 246, 255, 0.96) 100%); + color: #0f172a; + border-left: 1px solid rgba(15, 23, 42, 0.12); + box-shadow: -14px 0 34px rgba(15, 23, 42, 0.14); +} + +:root[data-theme="light"] .analysis-page .drawer-panel .sample-gallery, +:root[data-theme="light"] .analysis-page .drawer-panel .sample-gallery p, +:root[data-theme="light"] .analysis-page .drawer-panel .sample-gallery span { + color: #0f172a; +} + +:root[data-theme="light"] .analysis-page .drawer-panel .sample-gallery h2 { + background: linear-gradient(180deg, rgba(255, 255, 255, 0.92), rgba(241, 245, 249, 0.88)); + color: #0f172a; + border: 1px solid rgba(15, 23, 42, 0.08); + box-shadow: 0 10px 24px rgba(15, 23, 42, 0.08); + text-shadow: none; +} + +:root[data-theme="light"] .analysis-page .drawer-panel .sample-gallery .sample-card { + background: rgba(255, 255, 255, 0.96); + border-color: rgba(15, 23, 42, 0.12); + box-shadow: 0 10px 24px rgba(15, 23, 42, 0.12); +} + +:root[data-theme="light"] .analysis-page .drawer-panel .sample-gallery .sample-img { + background: #f8fafc; +} + +:root[data-theme="light"] .analysis-page .drawer-panel .sample-gallery .sample-card__select { + background: #0f172a; + color: #ffffff; + border-color: rgba(15, 23, 42, 0.18); + box-shadow: 0 10px 22px rgba(15, 23, 42, 0.18); +} + +:root[data-theme="light"] .analysis-page .drawer-panel::-webkit-scrollbar-thumb { + background: rgba(15, 23, 42, 0.28); +} + +:root[data-theme="light"] .analysis-page .drawer-panel::-webkit-scrollbar-track { + background: rgba(226, 232, 240, 0.78); +} + +:root[data-theme="dark"] .analysis-page .drawer-panel { + background: + radial-gradient(circle at 50% 0%, rgba(224, 168, 46, 0.09), transparent 32%), + linear-gradient(180deg, #111827 0%, #0b1020 48%, #080d18 100%); + color: #f8fafc; + border-left-color: rgba(226, 232, 240, 0.16); + box-shadow: -14px 0 34px rgba(15, 23, 42, 0.20); +} + +/* Analysis mobile + response completion (v35). + * Keep the route high in the viewport, make the sample picker a real mobile + * sheet instead of a narrow side rail, and leave desktop drawer geometry intact. */ +.analysis-page.home-container { + padding-top: clamp(6px, 1.2vh, 14px); + gap: 8px; +} + +.analysis-page .animated-header { + min-height: 0; + margin-bottom: 4px; +} + +.analysis-page .top-texts { + margin-bottom: 8px; +} + +@media (max-width: 980px) { + .analysis-page .drawer-panel, + :root[data-theme="light"] .analysis-page .drawer-panel, + :root[data-theme="dark"] .analysis-page .drawer-panel { + position: fixed; + inset: var(--site-header-height, 64px) 0 0; + width: 100%; + height: auto; + max-height: none; + padding: 18px clamp(14px, 4vw, 28px) max(24px, env(safe-area-inset-bottom)); + border-left: 0; + border-top: 1px solid var(--tz-ui-border, rgba(226, 232, 240, 0.18)); + overflow-x: hidden; + overflow-y: auto; + overscroll-behavior: contain; + box-sizing: border-box; + z-index: 550; + } + + .analysis-page .drawer-panel .sample-gallery { + width: min(720px, 100%); + margin-inline: auto; + } + + .analysis-page .drawer-panel .sample-gallery h2, + :root[data-theme="light"] .analysis-page .drawer-panel .sample-gallery h2, + :root[data-theme="dark"] .analysis-page .drawer-panel .sample-gallery h2 { + position: static; + margin: 0 52px 16px; + padding: 8px 12px 12px; + border-radius: 14px; + } + + .analysis-page .drawer-panel .sample-gallery .sample-grid, + :root[data-theme="light"] .analysis-page .drawer-panel .sample-gallery .sample-grid, + :root[data-theme="dark"] .analysis-page .drawer-panel .sample-gallery .sample-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 14px; + } + + .analysis-page .drawer-panel .sample-gallery .sample-card, + :root[data-theme="light"] .analysis-page .drawer-panel .sample-gallery .sample-card, + :root[data-theme="dark"] .analysis-page .drawer-panel .sample-gallery .sample-card { + height: clamp(170px, 31dvh, 240px); + } + + .analysis-page .drawer-toggle-wrapper { + top: calc(var(--site-header-height, 64px) + 12px); + right: 12px !important; + transform: none; + z-index: 570; + } + + .analysis-page .drawer-toggle-button, + :root[data-theme="light"] .analysis-page .drawer-toggle-button, + :root[data-theme="dark"] .analysis-page .drawer-toggle-button { + width: 42px; + height: 42px; + border-radius: 12px; + font-size: 20px; + } + + .analysis-page.drawer-open .result-wrapper.analysis-grid, + .analysis-page.drawer-open .result-wrapper.grid { + width: min(720px, calc(100vw - 32px)); + margin-inline: auto; + } +} + +@media (max-width: 520px) { + .analysis-page .drawer-panel .sample-gallery .sample-grid, + :root[data-theme="light"] .analysis-page .drawer-panel .sample-gallery .sample-grid, + :root[data-theme="dark"] .analysis-page .drawer-panel .sample-gallery .sample-grid { + grid-template-columns: minmax(0, 1fr); + } + + .analysis-page .drawer-panel .sample-gallery .sample-card, + :root[data-theme="light"] .analysis-page .drawer-panel .sample-gallery .sample-card, + :root[data-theme="dark"] .analysis-page .drawer-panel .sample-gallery .sample-card { + height: clamp(180px, 38dvh, 250px); + } + + .analysis-page .analysis-popup__card { + grid-template-columns: 1fr; + text-align: center; + } + + .analysis-page .analysis-popup__icon, + .analysis-page .analysis-popup__button { + justify-self: center; + } +} diff --git a/frontend/src/assets/styles/ImageDescriptorHeader.css b/frontend/src/assets/styles/ImageDescriptorHeader.css index d6279e0..a200a47 100644 --- a/frontend/src/assets/styles/ImageDescriptorHeader.css +++ b/frontend/src/assets/styles/ImageDescriptorHeader.css @@ -43,3 +43,19 @@ opacity: 1; } } + +/* QA follow-up: Analysis heading must be readable in both themes. */ +.analysis-page .animated-header h1 { + color: var(--tz-ui-text, #f8fafc); + border-bottom-color: var(--tz-turmeric, #e0a82e); + text-shadow: 0 2px 14px rgba(0, 0, 0, 0.24); +} + +:root[data-theme="light"] .analysis-page .animated-header h1 { + color: #0f172a; + text-shadow: none; +} + +:root[data-theme="dark"] .analysis-page .animated-header h1 { + color: #f8fafc; +} diff --git a/frontend/src/assets/styles/ImagePreviewPanel.css b/frontend/src/assets/styles/ImagePreviewPanel.css index aa52d50..30d8546 100644 --- a/frontend/src/assets/styles/ImagePreviewPanel.css +++ b/frontend/src/assets/styles/ImagePreviewPanel.css @@ -321,3 +321,109 @@ :root[data-theme="light"] .analysis-page .analysis-btn.long { color: #ffffff ; } + +/* QA follow-up: Analysis action buttons align with Prev/Next and stay yellow in both themes. */ +.analysis-page .preview-buttons-container { + margin-top: 18px; + transform: none; + min-height: 46px; + display: flex; + align-items: center; + justify-content: center; + gap: 12px; +} + +.analysis-page .preview-buttons-container .analysis-btn, +.analysis-page .preview-buttons-container .analysis-btn.short, +.analysis-page .preview-buttons-container .analysis-btn.long { + min-height: 44px; + min-width: 150px; + border-radius: 999px; + border: 1px solid var(--tz-turmeric, #e0a82e); + background: linear-gradient(135deg, var(--tz-turmeric, #e0a82e), var(--tz-turmeric-soft, #f59e0b)); + color: #ffffff; + font-weight: 850; + box-shadow: 0 10px 24px rgba(224, 168, 46, 0.24); +} + +:root[data-theme="dark"] .analysis-page .preview-buttons-container .analysis-btn, +:root[data-theme="dark"] .analysis-page .preview-buttons-container .analysis-btn.short, +:root[data-theme="dark"] .analysis-page .preview-buttons-container .analysis-btn.long { + color: #171101; +} + +/* Senior QA final: Short/Long controls align visually with Prev/Next and keep one button language. */ +.analysis-page .preview-buttons-container { + min-height: 46px; + display: flex; + align-items: center; + justify-content: center; + gap: 12px; + margin-top: 18px; + transform: translateY(2px); +} + +.analysis-page .analysis-btn, +.analysis-page .analysis-btn.short, +.analysis-page .analysis-btn.long { + min-height: 44px; + min-width: 142px; + padding: 0 18px; + border-radius: 999px; + border: 1px solid var(--tz-action-yellow, #e0a82e); + background: linear-gradient(135deg, var(--tz-action-yellow, #e0a82e), var(--tz-action-yellow-hover, #f0c463)); + color: var(--tz-action-yellow-text, #171101); + font-weight: 900; + box-shadow: 0 10px 24px rgba(224, 168, 46, 0.24); +} + +.analysis-page .analysis-btn:not(.disabled):hover { + transform: translateY(-1px); + box-shadow: 0 14px 30px rgba(224, 168, 46, 0.30); +} + +.analysis-page .analysis-btn.disabled, +.analysis-page .analysis-btn:disabled { + opacity: 0.68; + color: rgba(23, 17, 1, 0.72); + box-shadow: none; +} + +@media (max-width: 720px) { + .analysis-page .preview-buttons-container { + flex-wrap: wrap; + } +} + +/* Senior QA top-up: Short/Long buttons stay readable and vertically synced with Prev/Next. */ +.analysis-page .preview-buttons-container { + margin-top: 18px; + min-height: 46px; +} + +.analysis-page .analysis-btn, +.analysis-page .analysis-btn.short, +.analysis-page .analysis-btn.long { + color: var(--tz-action-yellow-text, #171101); +} + +:root[data-theme="light"] .analysis-page .analysis-btn, +:root[data-theme="light"] .analysis-page .analysis-btn.short, +:root[data-theme="light"] .analysis-page .analysis-btn.long { + color: #171101; + background: linear-gradient(135deg, #e0a82e, #d9900d); +} + +:root[data-theme="dark"] .analysis-page .analysis-btn, +:root[data-theme="dark"] .analysis-page .analysis-btn.short, +:root[data-theme="dark"] .analysis-page .analysis-btn.long { + color: #171101; +} + +/* Analysis-page layout: keep the primary analysis actions above the image card. + The component markup follows the same visual order, so keyboard navigation + and screen-reader reading order remain predictable. */ +.analysis-page .image-preview-container > .preview-buttons-container { + margin: 0 0 18px; + transform: none; +} diff --git a/frontend/src/assets/styles/Messages.css b/frontend/src/assets/styles/Messages.css index 787e851..d755332 100644 --- a/frontend/src/assets/styles/Messages.css +++ b/frontend/src/assets/styles/Messages.css @@ -650,3 +650,46 @@ :root[data-theme="light"] .message-wrapper .quick-replies-prompt { color: #334155; } + +/* QA follow-up: keep "Want to know more?" visible in dark theme. */ +:root[data-theme="dark"] .message-wrapper .inline-quick-replies { + background: rgba(15, 23, 42, 0.84); + border: 1px solid rgba(226, 232, 240, 0.18); +} + +:root[data-theme="dark"] .message-wrapper .quick-replies-prompt { + color: #f8fafc; + opacity: 1; + text-shadow: none; +} + +/* Senior QA final: inline quick reply prompt and buttons must be readable, not decorative noise. */ +.message-wrapper .inline-quick-replies { + background: var(--tz-ui-surface-raised, rgba(15, 23, 42, 0.88)); + border: 1px solid var(--tz-ui-border, rgba(226, 232, 240, 0.20)); + box-shadow: 0 12px 26px rgba(15, 23, 42, 0.16); +} + +.message-wrapper .quick-replies-prompt { + color: var(--tz-ui-text, #f8fafc); + opacity: 1; +} + +.message-wrapper .inline-quick-replies .chip { + min-height: 34px; + border-radius: 999px; + font-weight: 850; + color: var(--tz-action-yellow-text, #171101); + background: linear-gradient(135deg, var(--tz-action-yellow, #e0a82e), var(--tz-action-yellow-hover, #f0c463)); + border: 1px solid rgba(180, 83, 9, 0.30); +} + +:root[data-theme="light"] .message-wrapper .inline-quick-replies { + background: #ffffff; + border-color: rgba(15, 23, 42, 0.14); + box-shadow: 0 10px 24px rgba(15, 23, 42, 0.08); +} + +:root[data-theme="light"] .message-wrapper .quick-replies-prompt { + color: #0f172a; +} diff --git a/frontend/src/assets/styles/MobileViewportStability.css b/frontend/src/assets/styles/MobileViewportStability.css new file mode 100644 index 0000000..7271589 --- /dev/null +++ b/frontend/src/assets/styles/MobileViewportStability.css @@ -0,0 +1,205 @@ +/* Mobile viewport stability (v38). + * Final, route-scoped overrides for genuinely narrow devices. These rules do + * not alter desktop geometry or any request, upload, analysis, reader, chat, + * navigation, or theme behavior. + */ + +@media (max-width: 340px) { + .app-wrapper--analysis .home-container.analysis-page { + box-sizing: border-box; + width: 100%; + max-width: 100%; + padding: 8px 10px 20px; + gap: 6px; + overflow-x: hidden; + } + + .analysis-page .header-wrapper, + .analysis-page .animated-header, + .analysis-page .animated-header h1, + .analysis-page .top-texts, + .analysis-page .result-wrapper.analysis-grid, + .analysis-page.drawer-open .result-wrapper.analysis-grid { + box-sizing: border-box; + min-width: 0; + max-width: 100%; + } + + .analysis-page .animated-header { + min-height: 0; + margin-bottom: 2px; + padding-inline: 40px; + } + + .analysis-page .animated-header h1 { + width: 100%; + margin: 0; + padding: 3px 2px 7px; + font-size: clamp(19px, 8vw, 25px); + line-height: 1.15; + letter-spacing: -0.035em; + white-space: normal; + overflow-wrap: anywhere; + } + + .analysis-page .top-texts { + width: 100%; + margin: 0 auto 6px; + padding-inline: 2px; + font-size: clamp(12px, 4.3vw, 14px); + line-height: 1.35; + overflow-wrap: anywhere; + } + + .analysis-page .result-wrapper.analysis-grid, + .analysis-page.drawer-open .result-wrapper.analysis-grid { + width: 100%; + margin-inline: auto; + gap: 12px; + } + + .analysis-page .image-preview-container > .preview-buttons-container, + .analysis-page .preview-buttons-container { + display: grid; + grid-template-columns: minmax(0, 1fr); + width: 100%; + min-height: 0; + margin: 0 0 12px; + gap: 8px; + transform: none; + } + + .analysis-page .preview-buttons-container .analysis-btn, + .analysis-page .preview-buttons-container .analysis-btn.short, + .analysis-page .preview-buttons-container .analysis-btn.long { + box-sizing: border-box; + width: 100%; + min-width: 0; + min-height: 40px; + padding: 0 12px; + font-size: 14px; + } + + .analysis-page .dropzone.big-drop, + .analysis-page .preview-image { + height: clamp(210px, 50dvh, 300px); + min-height: clamp(210px, 50dvh, 300px); + max-height: clamp(210px, 50dvh, 300px); + } + + /* The old fixed side tab crossed the title and action row on narrow phones. + Keep the same control and handler, but anchor it inside the title band. */ + .analysis-page .drawer-toggle-wrapper { + position: absolute; + top: 8px; + right: 8px !important; + bottom: auto; + transform: none; + } + + .analysis-page .drawer-toggle-button, + :root[data-theme="light"] .analysis-page .drawer-toggle-button, + :root[data-theme="dark"] .analysis-page .drawer-toggle-button { + width: 44px; + height: 44px; + border-radius: 14px; + font-size: 20px; + } + +} + +@media (max-width: 360px) { + .app-error { + box-sizing: border-box; + min-height: calc(100dvh - var(--site-header-height, 64px)); + align-items: start; + padding: 12px 8px 20px; + } + + .app-error__card { + width: 100%; + padding: 18px 12px; + border-radius: 16px; + } + + .app-error__card h1 { + margin-bottom: 8px; + font-size: clamp(1.35rem, 8vw, 1.75rem); + } + + .app-error__card p { + font-size: 13px; + line-height: 1.45; + } + + .app-error__actions { + display: grid; + grid-template-columns: minmax(0, 1fr); + width: 100%; + margin-top: 16px; + gap: 8px; + } + + .app-error__actions button, + .app-error__actions a { + box-sizing: border-box; + width: 100%; + min-width: 0; + } +} + +@media (max-width: 480px) { + .reader-landing { + padding: 12px 12px 20px !important; + } + + .reader-landing__header { + margin-bottom: 16px !important; + } + + .reader-landing__header h1 { + margin-bottom: 6px !important; + font-size: clamp(1.7rem, 8vw, 2.15rem) !important; + line-height: 1.08 !important; + } + + .reader-landing__header p { + font-size: 14px !important; + line-height: 1.4 !important; + } + + .reader-landing__grid { + gap: 12px !important; + } + + .reader-landing__tile { + min-height: 170px !important; + padding: 20px 16px !important; + border-radius: 20px !important; + } + + .reader-landing__tile-icon { + margin-bottom: 12px !important; + font-size: 54px !important; + } + + .reader-landing__tile-title { + margin-bottom: 6px !important; + font-size: 24px !important; + line-height: 1.1 !important; + } + + .reader-landing__tile-description { + font-size: 14px !important; + line-height: 1.35 !important; + } +} + +@media (max-width: 360px) and (max-height: 560px) { + .analysis-page .dropzone.big-drop, + .analysis-page .preview-image { + height: 190px; + min-height: 190px; + max-height: 190px; + } +} diff --git a/frontend/src/assets/styles/ReaderAnalysisContrastFixes.css b/frontend/src/assets/styles/ReaderAnalysisContrastFixes.css new file mode 100644 index 0000000..3641907 --- /dev/null +++ b/frontend/src/assets/styles/ReaderAnalysisContrastFixes.css @@ -0,0 +1,99 @@ +/* + * Narrow visual corrections loaded after SeniorUiFixes.css. + * No reader, OCR, crop, validation, API, or download behaviour is changed. + */ + +/* PAN and Aadhaar result actions use the same filled accent treatment as the + rest of the application. The shared accent tokens intentionally provide a + darker amber surface with white text in light mode and a brighter turmeric + surface with dark text in dark mode. */ +.reader-result-action-button { + background: linear-gradient( + 135deg, + var(--tz-accent-control-surface, var(--tz-turmeric)), + var(--tz-accent-control-surface-hover, var(--tz-turmeric-soft)) + ) !important; + border-color: var( + --tz-accent-control-surface, + var(--tz-turmeric) + ) !important; + text-shadow: none !important; +} + +:root[data-theme="light"] .reader-result-action-button { + color: #ffffff !important; + -webkit-text-fill-color: #ffffff !important; +} + +:root[data-theme="dark"] .reader-result-action-button { + color: #111827 !important; + -webkit-text-fill-color: #111827 !important; +} + +.reader-result-action-button:hover { + background: linear-gradient( + 135deg, + var(--tz-accent-control-surface-hover, var(--tz-turmeric-soft)), + var(--tz-accent-control-surface, var(--tz-turmeric)) + ) !important; + border-color: var( + --tz-accent-control-surface-hover, + var(--tz-turmeric-soft) + ) !important; +} + +.reader-result-action-button svg { + color: currentColor !important; + fill: none !important; + stroke: currentColor !important; +} + +/* Analysis-only accent correction. SeniorUiFixes intentionally uses a darker + light-theme amber globally; the Analysis workspace should match the lighter + turmeric treatment already used by the rest of the site. */ +:root[data-theme="light"] + .app-wrapper--analysis + :is( + .header-nav a.active, + .analysis-page .preview-buttons-container .analysis-btn, + .analysis-page .description-wrapper .nav-buttons .nav-btn + ) { + background: linear-gradient( + 135deg, + var(--tz-turmeric), + var(--tz-turmeric-soft) + ) !important; + border-color: var(--tz-turmeric) !important; + color: #171101 !important; + -webkit-text-fill-color: #171101 !important; + text-shadow: none !important; +} + +:root[data-theme="light"] + .app-wrapper--analysis + :is( + .analysis-page .preview-buttons-container .analysis-btn, + .analysis-page .description-wrapper .nav-buttons .nav-btn + ) + svg { + color: currentColor !important; + fill: currentColor !important; + stroke: currentColor !important; +} + +:root[data-theme="light"] + .app-wrapper--analysis + :is( + .header-nav a.active, + .analysis-page .preview-buttons-container .analysis-btn, + .analysis-page .description-wrapper .nav-buttons .nav-btn + ):hover:not(:disabled):not(.disabled) { + background: linear-gradient( + 135deg, + var(--tz-turmeric-soft), + var(--tz-turmeric) + ) !important; + border-color: var(--tz-turmeric-soft) !important; + color: #171101 !important; + -webkit-text-fill-color: #171101 !important; +} diff --git a/frontend/src/assets/styles/SampleImageGalleryCard.css b/frontend/src/assets/styles/SampleImageGalleryCard.css index f6f41fa..06442d5 100644 --- a/frontend/src/assets/styles/SampleImageGalleryCard.css +++ b/frontend/src/assets/styles/SampleImageGalleryCard.css @@ -245,3 +245,32 @@ color: #171101 ; border-color: #f0c463 ; } + +/* QA follow-up: keep the sample drawer title visible in dark theme. */ +.analysis-page .sample-gallery h2, +:root[data-theme="dark"] .analysis-page .sample-gallery h2 { + color: var(--tz-ui-text, #f8fafc); + text-shadow: 0 2px 12px rgba(0, 0, 0, 0.34); +} + +:root[data-theme="light"] .analysis-page .sample-gallery h2 { + color: #0f172a; + text-shadow: none; +} + +/* Senior QA final: sample drawer title and cards remain visible and balanced in dark mode. */ +.analysis-page .sample-gallery h2 { + color: var(--tz-ui-text, #f8fafc); + font-size: clamp(20px, 2vw, 24px); + font-weight: 900; + letter-spacing: -0.02em; +} + +:root[data-theme="light"] .analysis-page .sample-gallery h2 { + color: #0f172a; +} + +.analysis-page .sample-gallery .sample-card { + border: 1px solid var(--tz-ui-border, rgba(226, 232, 240, 0.20)); + background: var(--tz-ui-surface-raised, rgba(15, 23, 42, 0.92)); +} diff --git a/frontend/src/assets/styles/SearchSettingsIcon.css b/frontend/src/assets/styles/SearchSettingsIcon.css new file mode 100644 index 0000000..7df7216 --- /dev/null +++ b/frontend/src/assets/styles/SearchSettingsIcon.css @@ -0,0 +1,1386 @@ +/* Keep Search settings available without letting a full-width label compete + with the hero. The icon-only control lives on the page edge and opens its + existing database panel inward, so no settings behavior changes. */ +.fabric-search .fabric-search__top-bar { + min-height: 0; +} + +.fabric-search .fabric-search__settings, +.fabric-search.fabric-search--previewing .fabric-search__settings { + position: fixed; + top: clamp(76px, 8vh, 92px); + right: clamp(12px, 1.5vw, 24px); + z-index: 180; + display: block; + width: auto; + margin: 0; +} + +.fabric-search .settings-panel, +.fabric-search.fabric-search--previewing .settings-panel { + position: relative; + display: block; + width: 48px; + max-width: 48px; + overflow: visible; + contain: none; +} + +.fabric-search .settings-panel__toggle, +:root[data-theme="light"] .fabric-search .settings-panel__toggle, +:root[data-theme="dark"] .fabric-search .settings-panel__toggle { + display: grid; + place-items: center; + width: 48px; + min-width: 48px; + height: 48px; + min-height: 48px; + padding: 0; + gap: 0; + border-radius: 50%; + clip-path: none; + overflow: visible; + background: var(--tz-ui-surface-raised, rgba(15, 23, 42, 0.94)); + color: var(--tz-ui-text, #f8fafc); + border: 1px solid var(--tz-ui-border, rgba(226, 232, 240, 0.24)); + box-shadow: 0 12px 30px rgba(2, 6, 23, 0.24); + transition: + transform 160ms ease, + color 160ms ease, + background-color 160ms ease, + border-color 160ms ease, + box-shadow 160ms ease; +} + +:root[data-theme="light"] .fabric-search .settings-panel__toggle { + background: rgba(255, 255, 255, 0.96); + color: #0f172a; + border-color: rgba(15, 23, 42, 0.16); + box-shadow: 0 12px 28px rgba(15, 23, 42, 0.12); +} + +.fabric-search .settings-panel__toggle:hover { + transform: translateY(-2px); + color: var(--tz-action-yellow, #e0a82e); + border-color: color-mix( + in srgb, + var(--tz-action-yellow, #e0a82e) 62%, + transparent + ); + box-shadow: + 0 0 0 3px rgba(224, 168, 46, 0.14), + 0 16px 34px rgba(2, 6, 23, 0.28); +} + +.fabric-search .settings-panel__toggle:focus-visible { + outline: 3px solid + color-mix(in srgb, var(--tz-action-yellow, #e0a82e) 72%, white); + outline-offset: 3px; +} + +.fabric-search .settings-panel__toggle--open { + color: var(--tz-action-yellow, #e0a82e); + border-color: var(--tz-action-yellow, #e0a82e); + box-shadow: + 0 0 0 3px rgba(224, 168, 46, 0.16), + 0 16px 36px rgba(2, 6, 23, 0.3); +} + +.fabric-search .settings-panel__icon { + width: 21px; + height: 21px; + flex: none; + stroke-width: 2.1; + transition: transform 180ms ease; +} + +.fabric-search .settings-panel__toggle--open .settings-panel__icon { + transform: rotate(30deg); +} + +.fabric-search .settings-panel__content, +.fabric-search.fabric-search--previewing .settings-panel__content { + position: absolute; + top: calc(100% + 12px); + right: 0; + width: min(224px, calc(100vw - 32px)); + min-width: 0; + max-height: calc(100dvh - 148px); + margin: 0; + overflow: auto; + overscroll-behavior: contain; +} + +/* Keep the database menu as a narrow vertical utility card. Opening below the + side icon prevents the panel from spanning across and covering the Search + hero while preserving the existing database actions and notifications. */ +.fabric-search .settings-panel__content .db-panel { + display: flex; + flex-direction: column; + align-items: stretch; + width: 100%; + max-width: 100%; + gap: 12px; + padding: 16px; +} + +.fabric-search .settings-panel__content .db-panel__header, +.fabric-search .settings-panel__content .db-panel__actions, +.fabric-search .settings-panel__content .db-panel__notification { + width: 100%; +} + +.fabric-search .settings-panel__content .db-panel__actions { + display: flex; + flex-direction: column; + gap: 10px; +} + +.fabric-search .settings-panel__content .db-panel__btn { + justify-content: flex-start; + width: 100%; + min-width: 0; +} + +.fabric-search .settings-panel__content .db-panel__notification { + box-sizing: border-box; + overflow-wrap: anywhere; +} + +@media (max-width: 680px) { + .fabric-search .fabric-search__settings, + .fabric-search.fabric-search--previewing .fabric-search__settings { + top: 78px; + right: 10px; + } + + .fabric-search .settings-panel, + .fabric-search.fabric-search--previewing .settings-panel { + width: 44px; + max-width: 44px; + } + + .fabric-search .settings-panel__toggle, + :root[data-theme="light"] .fabric-search .settings-panel__toggle, + :root[data-theme="dark"] .fabric-search .settings-panel__toggle { + width: 44px; + min-width: 44px; + height: 44px; + min-height: 44px; + } + + .fabric-search .settings-panel__content, + .fabric-search.fabric-search--previewing .settings-panel__content { + top: calc(100% + 10px); + right: 0; + width: min(216px, calc(100vw - 20px)); + min-width: 0; + max-height: calc(100dvh - 140px); + } +} + +/* Route spacing: start Search content immediately below the shared navbar. + Route-level vh padding previously created an oversized blank band at wide + viewports and when the browser was zoomed out. */ +.fabric-search.fabric-search--text-results { + padding-top: 0; +} + +.fabric-search:not(.fabric-search--has-results):not(.fabric-search--previewing) + .fabric-search__body, +.fabric-search.fabric-search--text-results .fabric-search__body { + padding-top: 0; +} + +.fabric-search .hero__header { + padding-top: 0; + padding-bottom: clamp(14px, 2vh, 24px); +} + +@media (max-width: 680px) { + .fabric-search:not(.fabric-search--has-results):not(.fabric-search--previewing) + .fabric-search__body, + .fabric-search.fabric-search--text-results .fabric-search__body { + padding-top: 0; + } + + .fabric-search .hero__header { + padding-top: 0; + } +} + +/* Search interaction polish: keep action pills compact and keep the settings + utility panel viewport-bound in every Search state, including image preview. + These are presentation-only overrides; component behavior is unchanged. */ +.fabric-search .hero__search-row > .btn--primary, +.fabric-search .search-bar__text-row > .btn--primary, +.fabric-search .search-bar__image-actions > .btn--primary, +.fabric-search .image-preview__search-row > .btn--primary { + min-width: 118px; + min-height: 42px; + height: 42px; + padding: 0 20px; + line-height: 1; +} + +.fabric-search .image-preview__action-rail .btn { + min-width: 104px; + min-height: 40px; + height: 40px; + padding: 0 16px; + gap: 6px; + line-height: 1; +} + +/* The legacy preview stylesheet changes this dropdown to a static element. + Pinning the already-small vertical card to the viewport prevents it from + being clipped by preview/result containers or ancestor containment. */ +.fabric-search .settings-panel__content, +.fabric-search.fabric-search--previewing .settings-panel__content, +.fabric-search.fabric-search--has-results .settings-panel__content, +.fabric-search.fabric-search--text-results .settings-panel__content { + position: fixed !important; + top: clamp(136px, calc(8vh + 60px), 152px) !important; + right: clamp(12px, 1.5vw, 24px) !important; + left: auto !important; + z-index: 220 !important; + box-sizing: border-box; + width: min(224px, calc(100vw - 32px)) !important; + min-width: 0 !important; + max-width: min(224px, calc(100vw - 32px)) !important; + max-height: calc(100dvh - 164px) !important; + margin: 0 !important; + overflow: auto !important; + overscroll-behavior: contain; + transform: none !important; + clip-path: none !important; + contain: none !important; +} + +.fabric-search .fabric-search__settings, +.fabric-search .settings-panel, +.fabric-search.fabric-search--previewing .fabric-search__settings, +.fabric-search.fabric-search--previewing .settings-panel { + overflow: visible !important; + contain: none !important; + transform: none !important; + filter: none !important; +} + +@media (max-width: 680px) { + .fabric-search .hero__search-row > .btn--primary, + .fabric-search .search-bar__text-row > .btn--primary, + .fabric-search .search-bar__image-actions > .btn--primary, + .fabric-search .image-preview__search-row > .btn--primary { + min-width: 108px; + min-height: 40px; + height: 40px; + padding-inline: 16px; + } + + .fabric-search .image-preview__action-rail .btn { + min-width: 96px; + min-height: 38px; + height: 38px; + padding-inline: 13px; + } + + .fabric-search .settings-panel__content, + .fabric-search.fabric-search--previewing .settings-panel__content, + .fabric-search.fabric-search--has-results .settings-panel__content, + .fabric-search.fabric-search--text-results .settings-panel__content { + top: 132px !important; + right: 10px !important; + width: min(216px, calc(100vw - 20px)) !important; + max-width: min(216px, calc(100vw - 20px)) !important; + max-height: calc(100dvh - 146px) !important; + } +} + +/* v25: keep the Search CTA deliberately slim in the hero, sticky text bar, + and cropped-image action row. This only overrides presentation dimensions; + the existing Search buttons, disabled states, and handlers are unchanged. */ +.fabric-search .hero__search-row > .btn--primary, +.fabric-search .search-bar__text-row > .btn--primary, +.fabric-search .image-preview__search-row > .btn--primary { + width: auto; + min-width: 104px; + min-height: 38px; + height: 38px; + padding-inline: 15px; + gap: 6px; +} + +/* An older end-of-file rule fixes the hero CTA at 136px. Override its flex + basis so the v25 dimensions are visible instead of being masked. */ +.fabric-search .hero__search-row > .btn--primary { + flex: 0 0 104px; +} + +@media (max-width: 680px) { + .fabric-search .hero__search-row > .btn--primary, + .fabric-search .search-bar__text-row > .btn--primary, + .fabric-search .image-preview__search-row > .btn--primary { + min-width: 96px; + min-height: 36px; + height: 36px; + padding-inline: 13px; + } + + .fabric-search .hero__search-row > .btn--primary { + flex-basis: 96px; + } +} + +/* v26: inset the cropped-image limit/action row from both card edges and make + its Search CTA narrower. The shared row inset also moves the Limit label + away from the left border without changing slider or Search behavior. */ +.fabric-search .image-preview__search-row { + box-sizing: border-box; + padding-inline: clamp(18px, 2vw, 26px); +} + +.fabric-search .image-preview__search-row > .btn--primary { + width: 90px; + min-width: 90px; + min-height: 34px; + height: 34px; + padding-inline: 9px; + gap: 5px; + justify-self: end; + font-size: 13px; +} + +@media (max-width: 680px) { + .fabric-search .image-preview__search-row { + padding-inline: 12px; + } + + .fabric-search .image-preview__search-row > .btn--primary { + width: 86px; + min-width: 86px; + min-height: 34px; + height: 34px; + padding-inline: 8px; + } +} + +/* v27: present the zero-result message as the same strong error alert used by + the other routes. Search state and result handling remain unchanged. */ +.fabric-search .fabric-search__empty { + box-sizing: border-box; + width: min(860px, calc(100% - 36px)); + margin: clamp(24px, 4vh, 44px) auto 16px; + padding: 12px 16px; + background: var(--tz-error-surface, #dc2626) !important; + background-image: none !important; + color: var(--tz-error-text, #ffffff) !important; + -webkit-text-fill-color: var(--tz-error-text, #ffffff) !important; + border: 1px solid var(--tz-error-border, #991b1b) !important; + border-radius: 12px; + box-shadow: 0 10px 24px rgba(220, 38, 38, 0.24) !important; + text-align: center; + text-shadow: none !important; + mix-blend-mode: normal !important; + filter: none !important; + opacity: 1 !important; + font-weight: 600; +} + +@media (max-width: 680px) { + .fabric-search .fabric-search__empty { + width: calc(100% - 24px); + margin-top: 20px; + padding: 11px 14px; + } +} + +/* v28: correct the cropped-image Search pill's box model so its declared + height is the real outer height, then give the icon and label more horizontal + breathing room. Search behavior and the surrounding limit row are unchanged. */ +.fabric-search .image-preview__search-row > .btn--primary { + box-sizing: border-box; + width: 116px; + min-width: 116px; + min-height: 34px; + height: 34px; + padding: 0 13px; + gap: 7px; + line-height: 1; + white-space: nowrap; +} + +.fabric-search .image-preview__search-row > .btn--primary > span { + display: inline-flex; + align-items: center; + line-height: 1; +} + +@media (max-width: 680px) { + .fabric-search .image-preview__search-row > .btn--primary { + width: 112px; + min-width: 112px; + min-height: 32px; + height: 32px; + padding-inline: 12px; + gap: 6px; + } +} + +/* v41: mobile Search containment and control alignment. + Keep the settings trigger in normal Search flow at stacked widths, give the + post-result workspace an explicit scroll owner, and make the two image-mode + replacement actions use identical dimensions. Search state and handlers are + unchanged. */ +@media (max-width: 900px) { + .fabric-search .fabric-search__top-bar { + box-sizing: border-box; + display: flex; + justify-content: flex-end; + align-items: center; + width: 100%; + min-height: 48px; + padding: 0 2px 6px; + } + + .fabric-search .fabric-search__settings, + .fabric-search.fabric-search--previewing .fabric-search__settings { + position: static; + inset: auto; + z-index: auto; + display: flex; + justify-content: flex-end; + align-items: center; + width: 100%; + margin: 0; + } + + .fabric-search.fabric-search--has-results { + box-sizing: border-box; + flex: 0 0 auto; + width: 100%; + height: calc(100dvh - var(--site-header-height, 64px)); + min-height: 0; + max-height: calc(100dvh - var(--site-header-height, 64px)); + padding-top: 0; + overflow-x: hidden; + overflow-y: auto; + overscroll-behavior-y: auto; + -webkit-overflow-scrolling: touch; + touch-action: pan-y; + } + + .fabric-search.fabric-search--has-results .search-bar--sticky { + position: sticky; + top: 0; + z-index: 210; + } + + .fabric-search.fabric-search--has-results .fabric-search__body--results { + box-sizing: border-box; + flex: 0 0 auto; + width: 100%; + min-height: max-content; + padding-bottom: max(84px, calc(64px + env(safe-area-inset-bottom))); + overflow: visible; + } + + .fabric-search.fabric-search--has-results .results-section, + .fabric-search.fabric-search--has-results .result-grid--full { + min-height: max-content; + overflow: visible; + } + + .fabric-search .search-bar__image-row { + min-width: 0; + } + + .fabric-search .search-bar__image-actions { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + align-items: stretch; + width: min(100%, 290px); + min-width: 0; + gap: 8px; + } + + .fabric-search .search-bar__image-actions .search-bar__image-action { + box-sizing: border-box; + width: 100%; + min-width: 0; + max-width: none; + min-height: 40px; + height: 40px; + padding: 0 10px; + justify-content: center; + white-space: nowrap; + } +} + +@media (max-width: 480px) { + .fabric-search .search-bar__inner { + flex-wrap: wrap; + } + + .fabric-search .search-bar__image-row { + flex: 1 1 100%; + width: 100%; + } + + .fabric-search .search-bar__image-actions { + flex: 1 1 auto; + width: auto; + } + + .fabric-search .search-bar__inner > .btn--ghost { + margin-left: auto; + } +} + + +/* v42: place the Original Image actions over the image itself and keep mobile + category choices balanced. Presentation only; Search handlers and state are + unchanged. */ +:root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions { + position: relative; + display: grid; + grid-template-columns: minmax(0, 1fr); + place-items: center; + gap: 0; + padding: 0; + overflow: hidden; +} + +:root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__img { + grid-column: 1; + width: 100%; + max-width: 100%; + min-width: 0; +} + +:root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail { + position: absolute; + inset: 0; + z-index: 5; + display: block; + grid-column: 1; + grid-row: 1; + width: 100%; + min-width: 0; + height: 100%; + margin: 0; + padding: 0; + pointer-events: none; + transform: none; +} + +:root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn { + position: absolute; + right: 14px; + box-sizing: border-box; + width: auto; + min-width: 112px; + min-height: 40px; + height: 40px; + margin: 0; + padding: 0 15px; + justify-content: center; + border-radius: 999px; + pointer-events: auto; + white-space: nowrap; + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); +} + +:root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:first-child { + position: absolute; + top: 14px; + bottom: auto; + background: rgba(255, 255, 255, 0.9); + border-color: rgba(15, 23, 42, 0.18); + color: #111827; + box-shadow: 0 10px 24px rgba(15, 23, 42, 0.16); +} + +:root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:last-child { + position: absolute; + top: auto; + bottom: 14px; + background: rgba(15, 23, 42, 0.7); + border-color: rgba(255, 255, 255, 0.34); + color: #ffffff; + box-shadow: 0 10px 24px rgba(15, 23, 42, 0.24); +} + +:root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:first-child:hover:not(:disabled) { + background: rgba(255, 255, 255, 0.98); + border-color: rgba(15, 23, 42, 0.28); + color: #111827; +} + +:root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:last-child:hover:not(:disabled) { + background: rgba(15, 23, 42, 0.88); + border-color: rgba(255, 255, 255, 0.5); + color: #ffffff; +} + +@media (max-width: 480px) { + :root[data-theme] + .fabric-search.fabric-search + .category-picker:not(.category-picker--compact) + .category-picker__grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + align-items: stretch; + gap: 10px; + } + + :root[data-theme] + .fabric-search.fabric-search + .category-picker:not(.category-picker--compact) + .category-picker__chip { + box-sizing: border-box; + width: 100%; + min-width: 0; + justify-content: center; + } + + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn { + right: 10px; + min-width: 96px; + min-height: 36px; + height: 36px; + padding-inline: 12px; + } + + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:first-child { + top: 10px; + } + + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:last-child { + bottom: 10px; + } +} + + +/* v43: align preview actions with the route's theme controls, remove the + spacer above the post-crop workspace, and keep compact result categories in + the same balanced 2 × 2 mobile grid as the idle Search form. Presentation + only; Search state, crop handlers and filter behavior are unchanged. */ +:root[data-theme="dark"] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:first-child, +:root[data-theme="dark"] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:last-child { + background: linear-gradient( + 135deg, + var(--tz-action-yellow, #e0a82e), + var(--tz-action-yellow-hover, #f0c463) + ); + border-color: var(--tz-action-yellow, #e0a82e); + color: #111827; + text-shadow: none; + box-shadow: 0 10px 24px rgba(224, 168, 46, 0.26); +} + +:root[data-theme="light"] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:first-child, +:root[data-theme="light"] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:last-child { + background: linear-gradient( + 135deg, + var(--tz-accent-control-surface, #8a4f08), + var(--tz-accent-control-surface-hover, #a45d0a) + ); + border-color: var(--tz-accent-control-surface, #8a4f08); + color: #ffffff; + text-shadow: none; + box-shadow: 0 10px 24px rgba(138, 79, 8, 0.22); +} + +:root[data-theme="dark"] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:first-child:hover:not(:disabled), +:root[data-theme="dark"] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:last-child:hover:not(:disabled) { + background: linear-gradient( + 135deg, + var(--tz-action-yellow-hover, #f0c463), + #facc15 + ); + border-color: var(--tz-action-yellow-hover, #f0c463); + color: #111827; +} + +:root[data-theme="light"] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:first-child:hover:not(:disabled), +:root[data-theme="light"] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:last-child:hover:not(:disabled) { + background: linear-gradient( + 135deg, + var(--tz-accent-control-surface-hover, #a45d0a), + #b86a0c + ); + border-color: var(--tz-accent-control-surface-hover, #a45d0a); + color: #ffffff; +} + +:root[data-theme] + .fabric-search.fabric-search--previewing { + padding-top: 0; +} + +:root[data-theme] + .fabric-search.fabric-search--previewing + .fabric-search__body { + padding-top: 8px; +} + +@media (min-width: 901px) { + :root[data-theme] + .fabric-search.fabric-search--previewing + .fabric-search__top-bar { + min-height: 0; + height: 0; + margin-bottom: 8px; + padding: 0; + } +} + +@media (max-width: 680px) { + :root[data-theme] + .fabric-search.fabric-search + .category-picker--compact + .category-picker__grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + align-items: stretch; + gap: 10px; + } + + :root[data-theme] + .fabric-search.fabric-search + .category-picker--compact + .category-picker__chip { + box-sizing: border-box; + width: 100%; + min-width: 0; + justify-content: center; + } + + :root[data-theme] + .fabric-search.fabric-search + .category-picker--compact + .category-picker__apply { + grid-column: 1 / -1; + width: 100%; + } +} + + +/* v44: keep Search utilities anchored to the mobile page edge instead of + consuming a centred content row, and align the compact text-search CTA with + the right edge of its input. Presentation only; settings and Search handlers + are unchanged. */ +@media (max-width: 900px) { + .fabric-search .fabric-search__body { + position: relative; + } + + .fabric-search .fabric-search__top-bar, + .fabric-search.fabric-search--previewing .fabric-search__top-bar { + position: relative; + z-index: 220; + width: 100%; + min-height: 0; + height: 0; + margin: 0; + padding: 0; + pointer-events: none; + } + + .fabric-search .fabric-search__settings, + .fabric-search.fabric-search--previewing .fabric-search__settings { + position: fixed; + top: calc(var(--site-header-height, 64px) + 10px); + right: 12px; + bottom: auto; + left: auto; + z-index: 320; + display: block; + width: 40px; + max-width: 40px; + margin: 0; + pointer-events: auto; + } + + .fabric-search .settings-panel, + .fabric-search.fabric-search--previewing .settings-panel { + width: 40px; + max-width: 40px; + align-items: initial; + } + + .fabric-search .settings-panel__toggle, + :root[data-theme="light"] .fabric-search .settings-panel__toggle, + :root[data-theme="dark"] .fabric-search .settings-panel__toggle { + width: 40px; + min-width: 40px; + height: 40px; + min-height: 40px; + } + + .fabric-search .hero__search-row { + display: grid; + grid-template-columns: minmax(0, 1fr) auto auto; + align-items: center; + gap: 8px; + } + + .fabric-search .hero__search-input { + grid-column: 1 / -1; + grid-row: 1; + width: 100%; + min-width: 0; + } + + .fabric-search .hero__search-row > .btn--primary { + grid-column: 2; + grid-row: 2; + justify-self: end; + flex: none; + margin-left: auto; + } + + .fabric-search .hero__search-row > .hero__clear-btn { + grid-column: 3; + grid-row: 2; + justify-self: end; + } +} + +@media (max-width: 420px) { + .fabric-search .fabric-search__settings, + .fabric-search.fabric-search--previewing .fabric-search__settings { + right: 10px; + } +} + +/* v46: keep the compact mobile Search CTA right-aligned, but place it slightly + lower beneath the input and leave a deliberate gap before the Results slider. + Search state, validation, keyboard submission and click handlers are unchanged. */ +@media (max-width: 900px) { + .fabric-search .hero__search-row > .btn--primary { + justify-self: end; + margin-top: 6px; + margin-right: 0; + margin-bottom: 8px; + margin-left: auto; + } + + .fabric-search .hero__search-controls > .limit-slider { + margin-top: 2px; + } +} + + +/* v47: on mobile, place the text-search action at the left edge directly + beneath the input and leave a measured gap before the Results slider. + Search submission, validation and keyboard behavior are unchanged. */ +@media (max-width: 900px) { + .fabric-search .hero__search-row { + grid-template-columns: auto minmax(0, 1fr) auto; + } + + .fabric-search .hero__search-row > .btn--primary { + grid-column: 1; + grid-row: 2; + justify-self: start; + margin: 8px auto 2px 0; + } + + .fabric-search .hero__search-row > .hero__clear-btn { + grid-column: 3; + grid-row: 2; + justify-self: end; + } + + .fabric-search .hero__search-controls > .limit-slider { + margin-top: 0; + } +} + + +/* v48: refine Search control spacing, normalize category sizing, and keep + Original Image actions attached to the rendered image instead of the wider + preview frame. Presentation only; Search, crop and category handlers are + unchanged. */ +@media (max-width: 900px) { + .fabric-search .hero__search-row > .btn--primary { + margin: 14px auto 10px 0; + } + + .fabric-search + :is( + .hero__categories, + .image-preview__options, + .search-bar__filters + ) + .category-picker__grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + align-items: stretch; + gap: 10px; + } + + .fabric-search + :is( + .hero__categories, + .image-preview__options, + .search-bar__filters + ) + .category-picker__chip { + box-sizing: border-box; + width: 100%; + min-width: 0; + min-height: 40px; + height: 40px; + padding-inline: 12px; + justify-content: center; + } + + .fabric-search + :is( + .hero__categories, + .image-preview__options, + .search-bar__filters + ) + .category-picker__apply { + grid-column: 1 / -1; + width: 100%; + } +} + +@media (min-width: 901px) { + .fabric-search + :is( + .hero__categories, + .image-preview__options, + .search-bar__filters + ) + .category-picker__grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + align-items: stretch; + gap: clamp(10px, 1vw, 16px); + } + + .fabric-search + :is( + .hero__categories, + .image-preview__options, + .search-bar__filters + ) + .category-picker__chip { + box-sizing: border-box; + width: 100%; + min-width: 0; + justify-content: center; + } + + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions { + position: relative; + display: grid; + width: fit-content; + max-width: 100%; + height: min(460px, 52vh); + margin-inline: auto; + place-items: center; + overflow: hidden; + } + + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__img { + width: auto; + max-width: 100%; + height: 100%; + max-height: 100%; + object-fit: contain; + } + + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + } +} + +@media (min-width: 901px) and (max-height: 700px) { + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions { + height: 42vh; + } +} + + +/* v49: compact the Search category controls and Search CTAs, and give Recrop + a clearer optical inset from the Original Image frame. Presentation only; + category selection, crop actions, Search submission and disabled states are + unchanged. */ +.fabric-search + :is(.hero__categories, .image-preview__options) + .category-picker { + padding: 12px 14px; +} + +.fabric-search + :is(.hero__categories, .image-preview__options) + .category-picker__header { + margin-bottom: 8px; +} + +.fabric-search + :is(.hero__categories, .image-preview__options) + .category-picker__toggle-all { + padding-inline: 9px; +} + +.fabric-search .hero__search-row > .btn--primary, +.fabric-search .search-bar__text-row > .btn--primary, +.fabric-search .image-preview__search-row > .btn--primary { + box-sizing: border-box; + width: 88px; + min-width: 88px; + max-width: 88px; + padding-inline: 10px; + flex-basis: 88px; +} + +@media (min-width: 901px) { + .fabric-search + :is(.hero__categories, .image-preview__options) + .category-picker__grid { + grid-template-columns: repeat(4, minmax(0, 190px)); + justify-content: space-between; + gap: 10px; + } + + .fabric-search + :is(.hero__categories, .image-preview__options) + .category-picker__chip { + min-height: 36px; + height: 36px; + padding-inline: 10px; + font-size: 11px; + } + + .fabric-search .hero__search-row > .btn--primary { + flex: 0 0 88px; + } + + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:last-child { + bottom: 20px; + } +} + +@media (max-width: 900px) { + .fabric-search + :is(.hero__categories, .image-preview__options) + .category-picker__chip { + min-height: 36px; + height: 36px; + padding-inline: 10px; + font-size: 11px; + } + + .fabric-search .hero__search-row > .btn--primary, + .fabric-search .search-bar__text-row > .btn--primary, + .fabric-search .image-preview__search-row > .btn--primary { + width: 84px; + min-width: 84px; + max-width: 84px; + padding-inline: 8px; + flex-basis: 84px; + } + + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:last-child { + bottom: 14px; + } +} + + +/* v50: restore the compact, left-grouped desktop category placement shown in + the intended Search layout and mirror the Original Image action spacing + across the complete frame. Presentation only; category, Search and crop + handlers remain unchanged. */ +@media (min-width: 901px) { + .fabric-search + :is(.hero__categories, .image-preview__options) + .category-picker__grid { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + align-items: center; + gap: 8px; + } + + .fabric-search + :is(.hero__categories, .image-preview__options) + .category-picker__chip { + flex: 0 0 auto; + width: auto; + min-width: 112px; + max-width: none; + } +} + +:root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:first-child { + top: 14px; +} + +:root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:last-child { + /* The preview header is 50px high. Adding the same 14px image inset makes + Recrop's distance from the outer bottom border match Clear's distance + from the outer top border. */ + bottom: 64px; +} + +@media (max-width: 480px) { + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:first-child { + top: 10px; + } + + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:last-child { + bottom: 60px; + } +} + + +/* v51: make the Original Image action insets genuinely symmetrical within + the media border and give only the post-crop Search CTA a small width + increase. Presentation only; Clear, Recrop and Search handlers remain + unchanged. */ +:root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:first-child { + top: 14px; +} + +:root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:last-child { + bottom: 14px; +} + +.fabric-search .image-preview__search-row > .btn--primary { + width: 96px; + min-width: 96px; + max-width: 96px; + padding-inline: 12px; + flex-basis: 96px; +} + +@media (max-width: 900px) { + .fabric-search .image-preview__search-row > .btn--primary { + width: 92px; + min-width: 92px; + max-width: 92px; + padding-inline: 10px; + flex-basis: 92px; + } +} + +@media (max-width: 480px) { + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:first-child { + top: 10px; + } + + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:last-child { + bottom: 10px; + } +} + + +/* v52: add an optical safety inset below Recrop so its shadow and rounded + edge no longer appear attached to the Original Image border, and widen only + the post-crop Search CTA slightly. Presentation only; crop and Search + handlers, disabled states and alignment remain unchanged. */ +:root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:last-child { + bottom: 28px; +} + +.fabric-search .image-preview__search-row > .btn--primary { + width: 108px; + min-width: 108px; + max-width: 108px; + padding-inline: 16px; + flex-basis: 108px; +} + +@media (max-width: 900px) { + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:last-child { + bottom: 20px; + } + + .fabric-search .image-preview__search-row > .btn--primary { + width: 104px; + min-width: 104px; + max-width: 104px; + padding-inline: 14px; + flex-basis: 104px; + } +} + +@media (max-width: 480px) { + :root[data-theme] + .fabric-search.fabric-search.fabric-search--previewing + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:last-child { + bottom: 16px; + } +} + + +/* v54: keep backend Search matches visible even when an individual remote + preview cannot load. The card remains in the result count and receives a + neutral placeholder instead of being removed and triggering a false + "No results found" state. Search requests and result data are unchanged. */ +.fabric-search .result-card__image-fallback { + box-sizing: border-box; + width: 100%; + height: 100%; + min-height: 0; + display: grid; + place-content: center; + justify-items: center; + gap: 8px; + padding: 18px; + color: var(--tz-ui-text-muted, var(--color-muted)); + background: var(--tz-ui-page, var(--color-bg, #0b1020)); + font-family: var(--font-mono); + font-size: 11px; + letter-spacing: 0.06em; + text-align: center; + text-transform: uppercase; +} + +.fabric-search .result-card__image-fallback > span:first-child { + font-size: 24px; + line-height: 1; +} diff --git a/frontend/src/assets/styles/SearchTextResultsLayout.css b/frontend/src/assets/styles/SearchTextResultsLayout.css new file mode 100644 index 0000000..4717dba --- /dev/null +++ b/frontend/src/assets/styles/SearchTextResultsLayout.css @@ -0,0 +1,58 @@ +/* Keep text-search results in the original Search workspace instead of + replacing the page with the compact sticky-results toolbar. */ +.fabric-search.fabric-search--text-results { + padding-top: clamp(22px, 4vh, 54px); +} + +.fabric-search.fabric-search--text-results .fabric-search__body { + width: min(100%, 1480px); + max-width: 1480px; + margin-inline: auto; + padding-inline: clamp(16px, 2vw, 28px); +} + +.fabric-search.fabric-search--text-results .hero { + width: min(100%, 1060px); + margin-inline: auto; +} + +/* v54: text-search results use the same centred canvas as the Search hero, + including at zoomed-out effective desktop widths. */ +.fabric-search.fabric-search--text-results .results-section { + box-sizing: border-box; + width: min(100%, 1060px); + max-width: 1060px; + margin: clamp(34px, 5vh, 64px) auto 0; +} + +.fabric-search.fabric-search--text-results .fabric-search__error, +.fabric-search.fabric-search--text-results .fabric-search__empty { + width: min(100%, 1060px); + margin-inline: auto; +} + +.fabric-search .hero__clear-btn { + flex: 0 0 auto; + min-width: 104px; + justify-content: center; +} + +@media (max-width: 680px) { + .fabric-search.fabric-search--text-results .fabric-search__body { + padding-inline: 14px; + } + + .fabric-search .hero__search-row { + flex-wrap: wrap; + } + + .fabric-search .hero__search-input { + flex-basis: 100%; + } + + .fabric-search .hero__search-row .btn--primary, + .fabric-search .hero__clear-btn { + flex: 1 1 0; + min-width: 0; + } +} diff --git a/frontend/src/assets/styles/SeniorUiFixes.css b/frontend/src/assets/styles/SeniorUiFixes.css new file mode 100644 index 0000000..adbb6c5 --- /dev/null +++ b/frontend/src/assets/styles/SeniorUiFixes.css @@ -0,0 +1,2390 @@ +/* + * Scoped UI corrections requested during senior review. + * + * This file is imported after the existing theme/makeover layer so these + * narrowly targeted rules win without increasing selector priority across + * unrelated pages. + */ + +/* Shared foreground for accent-filled controls. Keep this token limited to + controls whose surface is also an accent colour. Applying it to neutral + cards/chips caused unreadable white-on-white and black-on-navy states. */ +:root { + --tz-accent-control-foreground: #171101; + --tz-filled-control-foreground: var(--tz-accent-control-foreground); + --tz-action-yellow-text: var(--tz-accent-control-foreground); + --tz-ui-accent-contrast: var(--tz-accent-control-foreground); + --tz-accent-control-surface: #e0a82e; + --tz-accent-control-surface-hover: #f0c463; +} + +:root[data-theme="light"] { + --tz-accent-control-foreground: #ffffff; + --tz-accent-control-surface: #8a4f08; + --tz-accent-control-surface-hover: #a45d0a; +} + +:root[data-theme="dark"] { + --tz-accent-control-foreground: #111827; + --tz-accent-control-surface: #e0a82e; + --tz-accent-control-surface-hover: #f0c463; +} + +/* Only true accent actions inherit the requested theme-dependent foreground. + Neutral/ghost controls and coloured information chips define their own + contrast-safe foregrounds below. */ +:root[data-theme] + :is( + .analysis-page .analysis-btn, + .analysis-page .description-wrapper .nav-btn, + .analysis-page .drawer-toggle-button, + .analysis-page .sample-gallery .analyze-button, + .app-wrapper--chat .page-root .chat-card-actions .btn-secondary, + .app-wrapper--chat .suggestion-chips .chip.primary, + .message-wrapper .inline-quick-replies .chip, + .fabric-search .btn--primary, + .fabric-search .category-picker__apply.btn.btn--primary, + .grid-page .grid-controls.inline button:not(:disabled), + .upload-page .btn.primary, + .upload-page .btn.submit, + .upload-page .name-field .cancel, + .composer-modal__button--confirm, + .composer .locked-control-btn--confirm, + .composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__button--confirm + ) { + color: var(--tz-accent-control-foreground); +} + +/* Light-mode white labels require a darker amber surface than the legacy + turmeric token. Set foreground and surface together for every accent-filled + control, including active navigation and search/category actions. */ +:root[data-theme="light"] + :is( + .header-nav a.active, + .analysis-page .analysis-btn, + .analysis-page .description-wrapper .nav-btn, + .analysis-page .drawer-toggle-button, + .analysis-page .sample-gallery .analyze-button, + .app-wrapper--chat .page-root .chat-card-actions .btn-secondary, + .app-wrapper--chat .suggestion-chips .chip.primary, + .message-wrapper .inline-quick-replies .chip, + .fabric-search .btn--primary, + .fabric-search .btn-primary, + .fabric-search .search-btn, + .fabric-search .category-picker__chip--active, + .fabric-search .category-picker__apply.btn.btn--primary, + .grid-page .grid-controls.inline button:not(:disabled), + .upload-page .btn.primary, + .upload-page .btn.submit, + .upload-page .name-field .cancel, + .composer-modal__button--confirm, + .composer .locked-control-btn--confirm, + .composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__button--confirm + ) { + background: linear-gradient( + 135deg, + var(--tz-accent-control-surface), + var(--tz-accent-control-surface-hover) + ); + border-color: var(--tz-accent-control-surface); + color: var(--tz-accent-control-foreground); + text-shadow: none; +} + +/* Analysis: keep each action pair above its own card and anchor the actions to + the card's outer corners. These rules support both the original and the + already-reordered component markup. */ +.analysis-page .image-preview-container > .preview-buttons-container, +.analysis-page .description-wrapper > .nav-buttons { + order: 1; + align-self: stretch; + width: 100%; + min-height: 44px; + margin: 0 0 16px; + display: flex; + flex-flow: row nowrap; + align-items: center; + justify-content: space-between; + gap: 16px; + transform: none; +} + +/* Short and Long remain a single left-aligned action group. Prev/Next keep + their separate corner-aligned result navigation below. */ +.analysis-page .image-preview-container > .preview-buttons-container { + justify-content: flex-start; +} + +.analysis-page .image-preview-container > .preview-visual, +.analysis-page .description-wrapper > .description-box { + order: 2; +} + +.analysis-page .image-preview-container > .filesize-name { + order: 3; +} + +.analysis-page .preview-buttons-container > .validation-text { + width: 100%; + margin: 0; + text-align: center; +} + +/* Do not let legacy centering/flex rules pull the corner controls inward. */ +.analysis-page .preview-buttons-container > .analysis-btn, +.analysis-page .description-wrapper > .nav-buttons > .nav-btn { + flex: 0 0 auto; + margin: 0; +} + +/* Chat: slightly reduce visual weight and vertical density. This keeps the + existing layout and interaction model while fitting more conversation in the + viewport and reducing unnecessary scrolling on laptop screens. */ +.app-wrapper--chat .page-root .chat-card-title .main { + font-size: 14px; + font-weight: 700; + line-height: 1.25; +} + +.app-wrapper--chat .page-root .chat-card-actions .btn { + font-size: 13px; + font-weight: 700; +} + +.app-wrapper--chat .page-root .empty-state-large h3 { + margin-bottom: 7px; + font-size: clamp(20px, 2vw, 24px); + font-weight: 700; + line-height: 1.25; + letter-spacing: -0.015em; +} + +.app-wrapper--chat .page-root .empty-state-large p { + margin-bottom: 16px; + font-size: 14px; + font-weight: 400; + line-height: 1.4; +} + +.app-wrapper--chat .page-root .empty-state-large .chip, +.app-wrapper--chat .page-root .empty-state-large .chip:nth-child(odd), +.app-wrapper--chat .page-root .empty-state-large .chip:nth-child(even) { + min-height: 42px; + padding: 8px 12px; + font-size: 13px; + font-weight: 650; + line-height: 1.25; +} + +.app-wrapper--chat .message-wrapper .msg-row { + margin-block: 6px; +} + +.app-wrapper--chat .assistant-block, +.app-wrapper--chat .bubble.user { + font-size: 14px; + font-weight: 400; + line-height: 1.45; +} + +.app-wrapper--chat .assistant-block--with-copy { + padding: 9px 44px 9px 12px; +} + +.app-wrapper--chat .assistant-block .assistant-content p { + margin-bottom: 0.5rem; +} + +.app-wrapper--chat .assistant-block ul, +.app-wrapper--chat .assistant-block ol { + margin-block: 6px 9px; + line-height: 1.45; +} + +.app-wrapper--chat .assistant-block li { + margin-block: 4px; + font-size: 14px; +} + +.app-wrapper--chat .assistant-block h1, +.app-wrapper--chat .assistant-block h2, +.app-wrapper--chat .assistant-block h3, +.app-wrapper--chat .assistant-block h4 { + margin: 0.65rem 0 0.35rem; + font-weight: 700; + line-height: 1.3; +} + +.app-wrapper--chat .assistant-block h1 { + font-size: 1.35rem; +} + +.app-wrapper--chat .assistant-block h2 { + font-size: 1.2rem; +} + +.app-wrapper--chat .assistant-block h3, +.app-wrapper--chat .assistant-block h4 { + font-size: 1.08rem; +} + +/* Attachment preview: center the Remove label geometrically inside its pill. + Explicit sizing prevents inherited global button line-height/padding rules + from nudging the text vertically. */ +.app-wrapper--chat .composer .upload-remove { + box-sizing: border-box; + min-height: 34px; + padding: 0 12px; + display: inline-flex; + align-items: center; + justify-content: center; + line-height: 1; + text-align: center; +} + +@media (max-width: 720px) { + .analysis-page .image-preview-container > .preview-buttons-container, + .analysis-page .description-wrapper > .nav-buttons { + gap: 10px; + margin-bottom: 12px; + } + + .analysis-page .preview-buttons-container > .analysis-btn, + .analysis-page .description-wrapper > .nav-buttons > .nav-btn { + flex: 0 1 48%; + min-width: 0; + padding-inline: 12px; + } + + .app-wrapper--chat .page-root .empty-state-large h3 { + font-size: 20px; + } +} + +/* Follow-up: legacy Analysis CSS still centers the result navigation in some + viewport/theme combinations. The repeated class raises specificity above + route-level centering rules even when that stylesheet loads later. */ +.analysis-page .description-wrapper > .nav-buttons.nav-buttons { + box-sizing: border-box; + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + width: 100%; + max-width: none; + padding: 0; + justify-content: stretch; + transform: none; +} + +.analysis-page + .description-wrapper + > .nav-buttons.nav-buttons + > .nav-btn:first-child { + justify-self: start; +} + +.analysis-page + .description-wrapper + > .nav-buttons.nav-buttons + > .nav-btn:last-child { + justify-self: end; +} + +/* Chat attachment removal confirmation: remove hard-coded light-theme inline + colours while preserving the existing state and click handlers. */ +.composer-modal-overlay { + position: fixed; + inset: 0; + z-index: 1300; + padding: 16px; + display: flex; + align-items: center; + justify-content: center; +} + +.composer-modal-overlay .composer-modal--remove-attachment { + --remove-dialog-surface: #ffffff; + --remove-dialog-text: #0f172a; + --remove-dialog-muted: #475569; + --remove-dialog-media: #f1f5f9; + --remove-dialog-border: rgba(15, 23, 42, 0.16); + --remove-dialog-media-border: rgba(15, 23, 42, 0.12); + --remove-dialog-cancel: #ffffff; + + box-sizing: border-box; + width: min(520px, calc(100vw - 32px)); + max-width: 100%; + padding: 18px; + border: 1px solid var(--remove-dialog-border); + border-radius: 16px; + background: var(--remove-dialog-surface); + color: var(--remove-dialog-text); + box-shadow: 0 20px 56px rgba(15, 23, 42, 0.28); +} + +:root[data-theme="dark"] + .composer-modal-overlay + .composer-modal--remove-attachment { + --remove-dialog-surface: #0f172a; + --remove-dialog-text: #f8fafc; + --remove-dialog-muted: #cbd5e1; + --remove-dialog-media: #1e293b; + --remove-dialog-border: rgba(226, 232, 240, 0.24); + --remove-dialog-media-border: rgba(226, 232, 240, 0.2); + --remove-dialog-cancel: #1e293b; + + box-shadow: 0 22px 64px rgba(0, 0, 0, 0.5); +} + +.composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__layout { + display: grid; + grid-template-columns: 84px minmax(0, 1fr); + gap: 14px; + align-items: center; +} + +.composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__media { + width: 84px; + height: 84px; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid var(--remove-dialog-media-border); + border-radius: 10px; + background: var(--remove-dialog-media); + color: var(--remove-dialog-muted); + font-size: 13px; + font-weight: 700; + line-height: 1; +} + +.composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__content { + min-width: 0; +} + +.composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__title { + margin: 0 0 6px; + color: var(--remove-dialog-text); + font-size: 16px; + font-weight: 800; + line-height: 1.3; +} + +.composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__filename { + color: var(--remove-dialog-muted); + font-size: 14px; + font-weight: 500; + line-height: 1.4; + overflow-wrap: anywhere; +} + +.composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__actions { + margin-top: 14px; + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__button { + min-height: 38px; + padding: 0 14px; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 9px; + font-size: 14px; + font-weight: 800; + line-height: 1; + cursor: pointer; +} + +.composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__button--confirm { + border: 1px solid var(--tz-turmeric, #e0a82e); + background: var(--tz-turmeric, #e0a82e); + color: #171101; +} + +.composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__button--cancel { + border: 1px solid var(--remove-dialog-border); + background: var(--remove-dialog-cancel); + color: var(--remove-dialog-text); +} + +@media (max-width: 520px) { + .composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__layout { + grid-template-columns: 1fr; + text-align: center; + } + + .composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__media { + justify-self: center; + } + + .composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__actions { + justify-content: center; + } +} + +/* ============================================================ + Theme contrast regression guard + ============================================================ + These rules repair the controls shown in the supplied screenshots and the + same semantic control families elsewhere. They intentionally set surface and + foreground together so a future text-only theme override cannot recreate the + contrast failures. */ + +/* Analysis sample cards are neutral controls, not accent buttons. */ +:root[data-theme="light"] + .analysis-page + .drawer-panel + .sample-gallery + .sample-card + .sample-card__select { + background: #0f172a; + color: #ffffff; + border-color: rgba(15, 23, 42, 0.18); +} + +:root[data-theme="dark"] + .analysis-page + .drawer-panel + .sample-gallery + .sample-card + .sample-card__select { + background: #f8fafc; + color: #111827; + border-color: rgba(248, 250, 252, 0.92); +} + +:root[data-theme] + .analysis-page + .drawer-panel + .sample-gallery + .sample-card.sample-card:hover + .sample-card__select, +:root[data-theme] + .analysis-page + .drawer-panel + .sample-gallery + .sample-card.sample-card:focus-visible + .sample-card__select { + background: linear-gradient(135deg, #e0a82e, #f0c463); + color: #171101; + border-color: #f0c463; +} + +/* Chat landing prompts: pair each foreground with a deliberate surface. + Light mode uses strong coloured boxes with white labels; dark mode uses + pastel boxes with black labels, matching the requested theme direction. */ +:root[data-theme="light"] + .app-wrapper--chat + .page-root + .empty-state-large + .chip:nth-child(1) { + background: #9a3412; + border-color: #c2410c; + color: #ffffff; +} + +:root[data-theme="light"] + .app-wrapper--chat + .page-root + .empty-state-large + .chip:nth-child(2) { + background: #4338ca; + border-color: #4f46e5; + color: #ffffff; +} + +:root[data-theme="light"] + .app-wrapper--chat + .page-root + .empty-state-large + .chip:nth-child(3) { + background: #047857; + border-color: #059669; + color: #ffffff; +} + +:root[data-theme="light"] + .app-wrapper--chat + .page-root + .empty-state-large + .chip:nth-child(4) { + background: #be185d; + border-color: #db2777; + color: #ffffff; +} + +:root[data-theme="dark"] + .app-wrapper--chat + .page-root + .empty-state-large + .chip:nth-child(1) { + background: #fed7aa; + border-color: #fb923c; + color: #111827; +} + +:root[data-theme="dark"] + .app-wrapper--chat + .page-root + .empty-state-large + .chip:nth-child(2) { + background: #c7d2fe; + border-color: #818cf8; + color: #111827; +} + +:root[data-theme="dark"] + .app-wrapper--chat + .page-root + .empty-state-large + .chip:nth-child(3) { + background: #a7f3d0; + border-color: #34d399; + color: #111827; +} + +:root[data-theme="dark"] + .app-wrapper--chat + .page-root + .empty-state-large + .chip:nth-child(4) { + background: #fbcfe8; + border-color: #f472b6; + color: #111827; +} + +:root[data-theme] + .app-wrapper--chat + .page-root + .empty-state-large + .chip:disabled { + color: inherit; + opacity: 0.62; +} + +/* Assistant replies now follow the active theme instead of remaining a white + card in dark mode. Markdown content inherits the same readable foreground. */ +:root[data-theme="light"] .app-wrapper--chat .assistant-block { + background: #ffffff; + color: #0f172a; + border-color: rgba(15, 23, 42, 0.14); +} + +:root[data-theme="dark"] .app-wrapper--chat .assistant-block { + background: #171f33; + color: #f8fafc; + border-color: rgba(226, 232, 240, 0.22); +} + +:root[data-theme] + .app-wrapper--chat + .assistant-block + :is(.assistant-content, p, li, h1, h2, h3, h4, strong) { + color: inherit; +} + +:root[data-theme="light"] + .app-wrapper--chat + .assistant-block--with-copy + .copy-btn { + background: #f1f5f9; + color: #0f172a; + border-color: rgba(15, 23, 42, 0.14); +} + +:root[data-theme="dark"] + .app-wrapper--chat + .assistant-block--with-copy + .copy-btn { + background: #24304d; + color: #f8fafc; + border-color: rgba(226, 232, 240, 0.22); +} + +/* Upload primary action had black text on a translucent purple surface in dark + mode. Use the accent surface whenever the accent foreground token is used. */ +:root[data-theme="light"] .upload-page .btn.primary { + background: var(--tz-accent-control-surface, #8a4f08); + border-color: var(--tz-accent-control-surface, #8a4f08); + color: #ffffff; +} + +:root[data-theme="dark"] .upload-page .btn.primary { + background: var(--tz-turmeric, #e0a82e); + border-color: var(--tz-turmeric, #e0a82e); + color: #111827; +} + +:root[data-theme="light"] .upload-page .btn.submit, +:root[data-theme="light"] .upload-page .name-field .cancel { + background: var(--tz-accent-control-surface, #8a4f08); + border-color: var(--tz-accent-control-surface, #8a4f08); + color: #ffffff; +} + +:root[data-theme="dark"] .upload-page .btn.submit { + background: #38bdf8; + border-color: #38bdf8; + color: #111827; +} + +:root[data-theme="dark"] .upload-page .name-field .cancel { + background: #f8fafc; + border: 1px solid rgba(248, 250, 252, 0.86); + color: #111827; +} + +:root[data-theme="dark"] .upload-page .btn:not(.primary):not(.submit), +:root[data-theme="dark"] .upload-page .confirm-actions .btn-cancel { + background: #1e293b; + color: #f8fafc; + border-color: rgba(226, 232, 240, 0.24); +} + +:root[data-theme="light"] .upload-page .btn:not(.primary):not(.submit), +:root[data-theme="light"] .upload-page .confirm-actions .btn-cancel { + background: #ffffff; + color: #0f172a; + border-color: rgba(15, 23, 42, 0.16); +} + +/* Confirmation dialogs and locked composer controls use the same semantic + accent/neutral split in both themes. */ +:root[data-theme="light"] .composer-modal--confirm-upload, +:root[data-theme="light"] .upload-page .confirm-modal { + background: #ffffff; + color: #0f172a; + border-color: rgba(15, 23, 42, 0.16); +} + +:root[data-theme="dark"] .composer-modal--confirm-upload, +:root[data-theme="dark"] .upload-page .confirm-modal { + background: #0f172a; + color: #f8fafc; + border-color: rgba(226, 232, 240, 0.24); +} + +:root[data-theme] + .composer-modal--confirm-upload + :is(.composer-modal__title, .composer-modal__meta, .composer-modal__hint) { + color: inherit; +} + +:root[data-theme="light"] + .composer-modal--confirm-upload + .composer-modal__button--confirm, +:root[data-theme="light"] .composer .locked-control-btn--confirm { + background: var(--tz-accent-control-surface, #8a4f08); + border-color: var(--tz-accent-control-surface, #8a4f08); + color: #ffffff; +} + +:root[data-theme="dark"] + .composer-modal--confirm-upload + .composer-modal__button--confirm, +:root[data-theme="dark"] .composer .locked-control-btn--confirm { + background: var(--tz-turmeric, #e0a82e); + border-color: var(--tz-turmeric, #e0a82e); + color: #111827; +} + +:root[data-theme="light"] + .composer-modal--confirm-upload + .composer-modal__button--cancel, +:root[data-theme="light"] .composer .locked-control-btn--cancel { + background: #f8fafc; + color: #0f172a; + border-color: rgba(15, 23, 42, 0.16); +} + +:root[data-theme="dark"] + .composer-modal--confirm-upload + .composer-modal__button--cancel, +:root[data-theme="dark"] .composer .locked-control-btn--cancel { + background: #1e293b; + color: #f8fafc; + border-color: rgba(226, 232, 240, 0.24); +} + +/* ============================================================ + Analysis response position and Upload action hierarchy + ============================================================ */ + +/* Keep the response position beside the copy action in the lower-right corner + of the result card. The value is derived from the existing response history, + so Prev/Next behavior and regeneration logic remain unchanged. */ +.analysis-page .description-wrapper .response-copy-only { + justify-content: flex-end; + gap: 10px; +} + +.analysis-page .description-wrapper .response-copy-only .response-position { + min-width: 46px; + min-height: 34px; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0 10px; + border: 1px solid rgba(255, 255, 255, 0.28); + border-radius: 999px; + background: rgba(15, 23, 42, 0.72); + color: #f8fafc; + font-size: 0.875rem; + font-weight: 800; + line-height: 1; + letter-spacing: 0.02em; + font-variant-numeric: tabular-nums; +} + +.analysis-page .description-wrapper .response-copy-only .copy-btn { + margin-left: 0; +} + +/* Submit is the primary Upload action; Cancel is deliberately secondary. + Surface and foreground are always paired to avoid theme contrast regressions. */ +:root[data-theme="light"] .upload-page .name-field .btn.submit { + background: var(--tz-accent-control-surface, #8a4f08); + border-color: var(--tz-accent-control-surface, #8a4f08); + color: #ffffff; + box-shadow: 0 6px 18px rgba(138, 79, 8, 0.22); +} + +:root[data-theme="light"] .upload-page .name-field .cancel { + background: #ffffff; + border: 1px solid rgba(138, 79, 8, 0.48); + color: #7c4707; + box-shadow: 0 4px 14px rgba(15, 23, 42, 0.08); +} + +:root[data-theme="dark"] .upload-page .name-field .btn.submit { + background: var(--tz-turmeric, #e0a82e); + border-color: var(--tz-turmeric, #e0a82e); + color: #111827; + box-shadow: 0 6px 20px rgba(224, 168, 46, 0.22); +} + +:root[data-theme="dark"] .upload-page .name-field .cancel { + background: #1e293b; + border: 1px solid rgba(224, 168, 46, 0.48); + color: #f8fafc; + box-shadow: 0 4px 14px rgba(0, 0, 0, 0.2); +} + +:root[data-theme="light"] + .upload-page + .name-field + .btn.submit:hover:not(:disabled) { + background: var(--tz-accent-control-surface-hover, #a45d0a); + border-color: var(--tz-accent-control-surface-hover, #a45d0a); +} + +:root[data-theme="light"] + .upload-page + .name-field + .cancel:hover:not(:disabled) { + background: #fff7ed; + border-color: #8a4f08; + color: #6b3b05; +} + +:root[data-theme="dark"] + .upload-page + .name-field + .btn.submit:hover:not(:disabled) { + background: var(--tz-accent-control-surface-hover, #f0c463); + border-color: var(--tz-accent-control-surface-hover, #f0c463); +} + +:root[data-theme="dark"] .upload-page .name-field .cancel:hover:not(:disabled) { + background: #293548; + border-color: rgba(240, 196, 99, 0.72); + color: #ffffff; +} + +:root[data-theme] .upload-page .name-field .btn.submit:disabled { + opacity: 0.55; + box-shadow: none; +} + +/* ============================================================ + Requested cross-page control alignment and theme visibility + ============================================================ + Keep the latest review changes semantic and narrowly scoped. White-on-gold + actions use a deeper accessible gold; selected categories use a theme-aware + foreground so dark mode can retain dark ink on the brighter yellow. */ +:root { + --tz-white-on-gold-surface: #a65f00; + --tz-white-on-gold-surface-hover: #8a4f08; +} + +:root[data-theme="light"] { + --tz-selected-category-surface: #a65f00; + --tz-selected-category-foreground: #ffffff; +} + +:root[data-theme="dark"] { + --tz-selected-category-surface: var(--tz-turmeric, #e0a82e); + --tz-selected-category-foreground: #111827; +} + +/* Search image workspace: pin Clear Search and Recrop to the requested outer + corners of the Original Image frame, independent of image dimensions. */ +.fabric-search.fabric-search .image-preview__frame { + position: relative; +} + +.fabric-search.fabric-search .image-preview__frame-actions--top { + inset: 14px 14px auto auto; + margin: 0; +} + +.fabric-search.fabric-search .image-preview__frame-actions--bottom { + inset: auto 14px 14px auto; + margin: 0; +} + +:root[data-theme] + .fabric-search.fabric-search + .image-preview__frame-actions + .btn, +:root[data-theme] + .fabric-search.fabric-search + .search-bar__image-actions + :is(.btn--outline, .btn--ghost) { + background: var(--tz-white-on-gold-surface); + border-color: var(--tz-white-on-gold-surface); + color: #ffffff; + box-shadow: 0 10px 24px rgba(138, 79, 8, 0.24); + text-shadow: none; +} + +:root[data-theme] + .fabric-search.fabric-search + .image-preview__frame-actions + .btn:hover:not(:disabled), +:root[data-theme] + .fabric-search.fabric-search + .search-bar__image-actions + :is(.btn--outline, .btn--ghost):hover:not(:disabled) { + background: var(--tz-white-on-gold-surface-hover); + border-color: var(--tz-white-on-gold-surface-hover); + color: #ffffff; +} + +/* Selected Search categories: light theme uses white labels; dark theme uses + dark labels on the brighter yellow requested during review. */ +:root[data-theme] + .fabric-search.fabric-search + .category-picker__chip.category-picker__chip--active, +:root[data-theme] + .fabric-search.fabric-search + .category-picker__chip.category-picker__chip--active:hover { + background: var(--tz-selected-category-surface); + border-color: var(--tz-selected-category-surface); + color: var(--tz-selected-category-foreground); + box-shadow: 0 10px 24px rgba(166, 95, 0, 0.2); + text-shadow: none; +} + +:root[data-theme] + .fabric-search.fabric-search + .category-picker__chip.category-picker__chip--active + :is( + .category-picker__chip-check, + .category-picker__chip-icon, + .category-picker__chip-label + ) { + color: inherit; + opacity: 1; +} + +/* Search crop confirmation is intentionally white-on-gold in both themes. */ +:root[data-theme] + .fabric-search.fabric-search + .crop-drawer__actions + .btn.btn--primary { + background: var(--tz-white-on-gold-surface); + border-color: var(--tz-white-on-gold-surface); + color: #ffffff; + box-shadow: 0 10px 24px rgba(138, 79, 8, 0.24); +} + +:root[data-theme] + .fabric-search.fabric-search + .crop-drawer__actions + .btn.btn--primary:hover:not(:disabled) { + background: var(--tz-white-on-gold-surface-hover); + border-color: var(--tz-white-on-gold-surface-hover); + color: #ffffff; +} + +/* Upload dark theme: Submit and Cancel now share the exact Start Recording + surface and foreground while retaining their existing handlers/states. */ +:root[data-theme="dark"] + .upload-page.upload-page + :is( + .rec-controls .btn.primary, + .name-field .btn.submit, + .name-field .cancel + ) { + background: linear-gradient( + 135deg, + var(--tz-turmeric, #e0a82e), + var(--tz-turmeric-soft, #f0c463) + ); + border-color: var(--tz-turmeric, #e0a82e); + color: #111827; + box-shadow: 0 8px 20px rgba(224, 168, 46, 0.22); + text-shadow: none; +} + +:root[data-theme="dark"] + .upload-page.upload-page + :is( + .rec-controls .btn.primary, + .name-field .btn.submit, + .name-field .cancel + ):hover:not(:disabled) { + background: var(--tz-turmeric-soft, #f0c463); + border-color: var(--tz-turmeric-soft, #f0c463); + color: #111827; +} + +/* Analysis sample-image selection: light-theme hover/focus uses the requested + white label on a yellow surface. */ +:root[data-theme="light"] + .analysis-page.analysis-page + .drawer-panel + .sample-gallery + .sample-card.sample-card:hover + .sample-card__select, +:root[data-theme="light"] + .analysis-page.analysis-page + .drawer-panel + .sample-gallery + .sample-card.sample-card:focus-visible + .sample-card__select { + background: var(--tz-white-on-gold-surface); + border-color: var(--tz-white-on-gold-surface); + color: #ffffff; +} + +/* Analysis image-removal confirmation: both requested light-theme actions are + white-on-gold, with the dialog surface itself left unchanged. */ +:root[data-theme="light"] + .analysis-page.analysis-page + .confirm-actions + :is(.btn.btn-cancel, .btn.btn-confirm) { + background: var(--tz-white-on-gold-surface); + border-color: var(--tz-white-on-gold-surface); + color: #ffffff; + box-shadow: 0 6px 16px rgba(138, 79, 8, 0.2); +} + +:root[data-theme="light"] + .analysis-page.analysis-page + .confirm-actions + :is(.btn.btn-cancel, .btn.btn-confirm):hover:not(:disabled) { + background: var(--tz-white-on-gold-surface-hover); + border-color: var(--tz-white-on-gold-surface-hover); + color: #ffffff; +} + +/* Analysis header/navigation was inheriting muted route colours. Restore the + same strong, theme-aware typography used by the other application pages. */ +:root[data-theme="light"] + .app-wrapper--analysis + .site-header + :is(.brand-name, .header-nav a:not(.active)) { + color: #1f2937; + font-weight: 750; + opacity: 1; +} + +:root[data-theme="dark"] + .app-wrapper--analysis + .site-header + :is(.brand-name, .header-nav a:not(.active)) { + color: #f1f5f9; + font-weight: 750; + opacity: 1; +} + +:root[data-theme="light"] + .app-wrapper--analysis + .site-header + .header-nav + a.active { + background: var(--tz-white-on-gold-surface); + border-color: var(--tz-white-on-gold-surface); + color: #ffffff; + font-weight: 800; + opacity: 1; +} + +:root[data-theme="dark"] + .app-wrapper--analysis + .site-header + .header-nav + a.active { + background: var(--tz-turmeric, #e0a82e); + border-color: var(--tz-turmeric, #e0a82e); + color: #111827; + font-weight: 800; + opacity: 1; +} + +:root[data-theme="light"] + .app-wrapper--analysis + .analysis-page + :is(.animated-header h1, .top-texts) { + color: #0f172a; + opacity: 1; +} + +:root[data-theme="dark"] + .app-wrapper--analysis + .analysis-page + :is(.animated-header h1, .top-texts) { + color: #f8fafc; + opacity: 1; +} + +/* Chat sample prompts remain distinguishable but use balanced, less muddy + colours in both themes. */ +:root[data-theme="light"] + .app-wrapper--chat + .page-root + .empty-state-large + .chips + > .chip:nth-child(1) { + background: #b45309; + border-color: #d97706; + color: #ffffff; +} + +:root[data-theme="light"] + .app-wrapper--chat + .page-root + .empty-state-large + .chips + > .chip:nth-child(2) { + background: #4f46e5; + border-color: #6366f1; + color: #ffffff; +} + +:root[data-theme="light"] + .app-wrapper--chat + .page-root + .empty-state-large + .chips + > .chip:nth-child(3) { + background: #0f766e; + border-color: #0d9488; + color: #ffffff; +} + +:root[data-theme="light"] + .app-wrapper--chat + .page-root + .empty-state-large + .chips + > .chip:nth-child(4) { + background: #be185d; + border-color: #db2777; + color: #ffffff; +} + +:root[data-theme="dark"] + .app-wrapper--chat + .page-root + .empty-state-large + .chips + > .chip:nth-child(1) { + background: #fdba74; + border-color: #fb923c; + color: #111827; +} + +:root[data-theme="dark"] + .app-wrapper--chat + .page-root + .empty-state-large + .chips + > .chip:nth-child(2) { + background: #a5b4fc; + border-color: #818cf8; + color: #111827; +} + +:root[data-theme="dark"] + .app-wrapper--chat + .page-root + .empty-state-large + .chips + > .chip:nth-child(3) { + background: #6ee7b7; + border-color: #34d399; + color: #111827; +} + +:root[data-theme="dark"] + .app-wrapper--chat + .page-root + .empty-state-large + .chips + > .chip:nth-child(4) { + background: #f9a8d4; + border-color: #f472b6; + color: #111827; +} + +:root[data-theme] + .app-wrapper--chat + .page-root + .empty-state-large + .chips + > .chip:hover:not(:disabled) { + filter: brightness(1.06); + transform: translateY(-1px); +} + +/* ============================================================ + Search preview structure and compact category action follow-up + ============================================================ + Keep the title and its actions inside each image frame so their alignment is + independent of the uploaded image's aspect ratio. These rules are scoped to + the Search preview and do not affect result cards or the sticky toolbar. */ +:root[data-theme] .fabric-search.fabric-search .category-picker__toggle-all { + min-height: 32px; + height: 32px; + padding: 0 14px; + line-height: 1; +} + +:root[data-theme] .fabric-search.fabric-search .image-preview__frame { + display: flex; + flex-direction: column; + align-items: stretch; + justify-items: initial; +} + +:root[data-theme] .fabric-search.fabric-search .image-preview__frame-header { + display: grid; + grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); + align-items: center; + flex: 0 0 auto; + min-height: 50px; + padding: 8px 14px; + border-bottom: 1px solid var(--tz-ui-border, rgba(100, 116, 139, 0.22)); + background: transparent; +} + +:root[data-theme] .fabric-search.fabric-search .image-preview__pane-title { + grid-column: 2; + justify-self: center; + align-self: center; + display: block; + min-height: 0; + margin: 0; + padding: 0; + border: 0; + border-radius: 0; + background: transparent; + box-shadow: none; + color: var(--tz-ui-text, #f8fafc); + font-family: var(--font-sans, Inter, system-ui, sans-serif); + font-size: 13px; + font-weight: 900; + line-height: 1.2; + letter-spacing: 0.08em; + text-align: center; + text-shadow: none; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .image-preview__pane-title { + color: #0f172a; +} + +:root[data-theme] + .fabric-search.fabric-search + .image-preview__frame-header + .image-preview__frame-actions--top { + position: static; + grid-column: 3; + justify-self: end; + inset: auto; + margin: 0; +} + +:root[data-theme] .fabric-search.fabric-search .image-preview__media { + position: relative; + display: grid; + place-items: center; + flex: 1 1 auto; + min-height: 210px; + overflow: hidden; +} + +:root[data-theme] + .fabric-search.fabric-search + .image-preview__media + .image-preview__img { + height: min(410px, calc(52vh - 50px)); + max-height: min(410px, calc(52vh - 50px)); +} + +:root[data-theme] + .fabric-search.fabric-search + .image-preview__media + .image-preview__frame-actions--bottom { + position: absolute; + inset: auto 14px 14px auto; + margin: 0; +} + +/* Light mode uses a deeper accessible gold because the requested white label + does not meet contrast on the brighter dark-theme yellow. */ +:root[data-theme="light"] + .fabric-search.fabric-search + .crop-drawer__actions + .btn.btn--primary { + background: #a16207; + border-color: #a16207; + color: #ffffff; + text-shadow: none; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .crop-drawer__actions + .btn.btn--primary + span { + color: inherit; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .crop-drawer__actions + .btn.btn--primary:hover:not(:disabled) { + background: #854d0e; + border-color: #854d0e; + color: #ffffff; +} + +/* Clear/Recrop retain the project's theme convention: white on accessible gold + in light mode, dark ink on bright yellow in dark mode. */ +:root[data-theme="light"] + .fabric-search.fabric-search + .image-preview__frame-actions + .btn { + background: #a16207; + border-color: #a16207; + color: #ffffff; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .image-preview__frame-actions + .btn:hover:not(:disabled) { + background: #854d0e; + border-color: #854d0e; + color: #ffffff; +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .image-preview__frame-actions + .btn { + background: var(--tz-turmeric-soft, #f0c463); + border-color: var(--tz-turmeric-soft, #f0c463); + color: #111827; + text-shadow: none; +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .image-preview__frame-actions + .btn:hover:not(:disabled) { + background: #f6d889; + border-color: #f6d889; + color: #111827; +} + +@media (max-height: 700px) { + :root[data-theme] + .fabric-search.fabric-search + .image-preview__media + .image-preview__img { + height: calc(42vh - 50px); + max-height: calc(42vh - 50px); + } +} + +/* ============================================================ + Search render stability, text contrast, pagination, and shared + destructive-confirmation dialog follow-up + ============================================================ */ + +/* The hero upload control previously relied on hover styles to establish its + visible surface. Give it a complete non-hover state and avoid backdrop/GPU + effects that can leave the wide control partially unpainted until pointer + movement on some Windows/Chromium configurations. */ +:root[data-theme] + .fabric-search.fabric-search + .hero + .hero__search-controls + .hero__upload-btn.btn.btn--ghost { + box-sizing: border-box; + display: inline-flex; + align-items: center; + justify-content: center; + width: 100%; + min-height: 56px; + opacity: 1; + visibility: visible; + transform: none; + filter: none; + backdrop-filter: none; + -webkit-backdrop-filter: none; + isolation: isolate; + box-shadow: none; + transition: + background-color 160ms ease, + border-color 160ms ease, + color 160ms ease, + box-shadow 160ms ease; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .hero + .hero__search-controls + .hero__upload-btn.btn.btn--ghost { + background: #ffffff; + border: 1px solid rgba(138, 79, 8, 0.42); + color: #172033; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .hero + .hero__search-controls + .hero__upload-btn.btn.btn--ghost:hover:not(:disabled) { + background: #fffaf0; + border-color: #a16207; + color: #172033; + box-shadow: 0 8px 22px rgba(138, 79, 8, 0.12); + transform: none; +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .hero + .hero__search-controls + .hero__upload-btn.btn.btn--ghost { + background: #111827; + border: 1px solid rgba(240, 196, 99, 0.48); + color: #f8fafc; +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .hero + .hero__search-controls + .hero__upload-btn.btn.btn--ghost:hover:not(:disabled) { + background: #172033; + border-color: #f0c463; + color: #ffffff; + box-shadow: 0 8px 22px rgba(224, 168, 46, 0.14); + transform: none; +} + +/* Apply Filter follows the requested light-theme white-on-gold treatment. + Explicit child colours prevent older compact-toolbar rules from restoring + black text after lazy stylesheet loading. */ +:root[data-theme="light"] + .fabric-search.fabric-search + .category-picker.category-picker--compact + .category-picker__apply.btn.btn--primary { + background: linear-gradient(135deg, #a16207 0%, #b45309 100%); + border-color: #a16207; + color: #ffffff; + text-shadow: none; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .category-picker.category-picker--compact + .category-picker__apply.btn.btn--primary + :is(.category-picker__apply-label, .category-picker__apply-arrow) { + color: #ffffff; + text-shadow: none; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .category-picker.category-picker--compact + .category-picker__apply.btn.btn--primary:hover:not(:disabled) { + background: linear-gradient(135deg, #854d0e 0%, #a16207 100%); + border-color: #854d0e; + color: #ffffff; +} + +/* Search pagination should read as navigation text, not as another pair of + pill buttons. Keep the controls transparent and borderless while preserving + keyboard focus and clear enabled/disabled states in both themes. */ +:root[data-theme] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn { + min-height: 0; + padding: 6px 8px; + border: 0; + border-radius: 6px; + background: transparent; + box-shadow: none; + backdrop-filter: none; + -webkit-backdrop-filter: none; + text-shadow: none; + transform: none; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn { + color: #334155; +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn { + color: #f1f5f9; +} + +:root[data-theme] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:not(:disabled):hover { + border: 0; + background: transparent; + color: #b7791f; + box-shadow: none; + transform: none; +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:not(:disabled):hover { + color: #f0c463; +} + +:root[data-theme] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:disabled { + background: transparent; + border: 0; + box-shadow: none; + opacity: 0.42; +} + +:root[data-theme] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:focus-visible { + outline: 2px solid #e0a82e; + outline-offset: 2px; +} + +/* Upload and Analysis use the same destructive-confirmation language. A + neutral charcoal surface avoids the previous blue-vs-black inconsistency; + destructive intent remains red and Cancel remains neutral in both themes. */ +:root[data-theme] + :is( + .analysis-page.analysis-page .image-preview-container, + .upload-page.upload-page + ) + .confirm-overlay { + background: rgba(2, 6, 23, 0.72); + backdrop-filter: blur(3px); + -webkit-backdrop-filter: blur(3px); +} + +:root[data-theme] + :is( + .analysis-page.analysis-page .image-preview-container, + .upload-page.upload-page + ) + .confirm-modal { + width: min(420px, calc(100vw - 40px)); + max-width: 420px; + padding: 20px; + border: 1px solid rgba(148, 163, 184, 0.28); + border-radius: 12px; + background: #111827; + color: #f8fafc; + box-shadow: 0 18px 48px rgba(2, 6, 23, 0.5); +} + +:root[data-theme] + :is( + .analysis-page.analysis-page .image-preview-container, + .upload-page.upload-page + ) + :is(.confirm-title, .confirm-body) { + color: #f8fafc; + opacity: 1; + text-shadow: none; +} + +:root[data-theme] + :is( + .analysis-page.analysis-page .image-preview-container, + .upload-page.upload-page + ) + .confirm-actions + .btn.btn-cancel { + background: #374151; + border: 1px solid #4b5563; + color: #ffffff; + box-shadow: none; +} + +:root[data-theme] + :is( + .analysis-page.analysis-page .image-preview-container, + .upload-page.upload-page + ) + .confirm-actions + .btn.btn-confirm { + background: #dc2626; + border: 1px solid #dc2626; + color: #ffffff; + box-shadow: none; +} + +:root[data-theme] + :is( + .analysis-page.analysis-page .image-preview-container, + .upload-page.upload-page + ) + .confirm-actions + .btn.btn-cancel:hover:not(:disabled) { + background: #4b5563; + border-color: #64748b; + color: #ffffff; +} + +:root[data-theme] + :is( + .analysis-page.analysis-page .image-preview-container, + .upload-page.upload-page + ) + .confirm-actions + .btn.btn-confirm:hover:not(:disabled) { + background: #b91c1c; + border-color: #b91c1c; + color: #ffffff; +} + +/* ============================================================ + Search pagination, preview action spacing, and upload dialog centering + ============================================================ + These follow-up rules deliberately override the earlier transparent + pagination treatment and the absolute preview actions. They are limited to + the Search result controls and the Upload confirmation overlay. */ + +/* Prev/Next use the same filled accent language as the other Search actions. + The foreground follows the requested theme convention: white in light mode, + dark ink in dark mode. */ +:root[data-theme] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn { + min-height: 38px; + padding: 8px 16px; + border: 1px solid transparent; + border-radius: 999px; + box-shadow: 0 8px 20px rgba(138, 79, 8, 0.18); + backdrop-filter: none; + -webkit-backdrop-filter: none; + text-shadow: none; + transform: none; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn { + background: #a16207; + border-color: #a16207; + color: #ffffff; +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn { + background: var(--tz-turmeric-soft, #f0c463); + border-color: var(--tz-turmeric-soft, #f0c463); + color: #111827; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:not(:disabled):hover { + background: #854d0e; + border-color: #854d0e; + color: #ffffff; + box-shadow: 0 10px 24px rgba(133, 77, 14, 0.24); + transform: translateY(-1px); +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:not(:disabled):hover { + background: #f6d889; + border-color: #f6d889; + color: #111827; + box-shadow: 0 10px 24px rgba(240, 196, 99, 0.2); + transform: translateY(-1px); +} + +:root[data-theme] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:disabled { + border-color: currentColor; + box-shadow: none; + opacity: 0.5; + transform: none; +} + +/* Reserve a real action rail beside the Original Image instead of absolutely + pinning controls against the frame edge. This keeps Clear slightly below the + title, lifts Recrop away from the clipped lower edge, and remains stable for + tall, square, and landscape source images. */ +:root[data-theme] + .fabric-search.fabric-search + .image-preview__media--with-actions { + grid-template-columns: minmax(0, 1fr) minmax(112px, 132px); + gap: 12px; + padding: 12px 14px; + overflow: hidden; +} + +:root[data-theme] + .fabric-search.fabric-search + .image-preview__media--with-actions + .image-preview__img { + grid-column: 1; + width: 100%; + max-width: 100%; + min-width: 0; + object-fit: contain; +} + +:root[data-theme] + .fabric-search.fabric-search + .image-preview__media--with-actions + .image-preview__action-rail { + position: static; + inset: auto; + grid-column: 2; + align-self: stretch; + display: flex; + flex-direction: column; + align-items: stretch; + justify-content: space-evenly; + gap: 18px; + min-width: 0; + margin: 0; + padding: 16px 0; + z-index: 3; +} + +:root[data-theme] + .fabric-search.fabric-search + .image-preview__action-rail + .btn { + width: 100%; + min-width: 0; + justify-content: center; + white-space: nowrap; +} + +@media (max-width: 680px) { + :root[data-theme] + .fabric-search.fabric-search + .image-preview__media--with-actions { + grid-template-columns: minmax(0, 1fr); + } + + :root[data-theme] + .fabric-search.fabric-search + .image-preview__media--with-actions + .image-preview__action-rail { + grid-column: 1; + flex-direction: row; + justify-content: flex-end; + padding: 0; + } + + :root[data-theme] + .fabric-search.fabric-search + .image-preview__action-rail + .btn { + width: auto; + min-width: 104px; + } +} + +/* Keep Upload remove confirmations centered in the visible viewport even when + the page is scrolled. Dynamic viewport units avoid the dialog drifting low + when browser chrome changes the available height. */ +:root[data-theme] .upload-page.upload-page > .confirm-overlay { + position: fixed; + inset: 0; + width: 100vw; + min-width: 100vw; + height: 100vh; + min-height: 100vh; + height: 100dvh; + min-height: 100dvh; + display: grid; + place-items: center; + align-content: center; + justify-content: center; + margin: 0; + padding: 24px; + overflow: auto; + z-index: 10000; +} + +:root[data-theme] .upload-page.upload-page > .confirm-overlay > .confirm-modal { + position: relative; + inset: auto; + margin: 0; + max-height: calc(100dvh - 48px); + overflow: auto; + transform: none; +} + +/* ============================================================ + Search pagination persistence, preview action balance, and Clear Audio + ============================================================ + Keep disabled pagination controls visible, balance the Original Image + actions in a stable rail, and give Clear Audio the same visual hierarchy as + the Upload form actions. These rules are intentionally page-scoped. */ + +/* Both pagination controls remain visible on every page. Disabled state is + communicated through cursor and a softer surface—not by disappearing. */ +:root[data-theme="light"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn, +:root[data-theme="light"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:disabled, +:root[data-theme="light"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:disabled:hover { + background: #a16207; + border-color: #a16207; + color: #ffffff; + opacity: 1; + visibility: visible; + box-shadow: 0 8px 20px rgba(138, 79, 8, 0.18); +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn, +:root[data-theme="dark"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:disabled, +:root[data-theme="dark"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:disabled:hover { + background: var(--tz-turmeric-soft, #f0c463); + border-color: var(--tz-turmeric-soft, #f0c463); + color: #111827; + opacity: 1; + visibility: visible; + box-shadow: 0 8px 20px rgba(240, 196, 99, 0.18); +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:not(:disabled):hover { + background: #854d0e; + border-color: #854d0e; + color: #ffffff; +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:not(:disabled):hover { + background: #f6d889; + border-color: #f6d889; + color: #111827; +} + +:root[data-theme] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:disabled { + cursor: not-allowed; + filter: saturate(0.72); +} + +/* Center Clear and Recrop as one balanced vertical group. The rail remains + independent from image dimensions, so neither action can be clipped. */ +:root[data-theme] + .fabric-search.fabric-search + .image-preview__media--with-actions + .image-preview__action-rail { + justify-content: center; + gap: 28px; + padding: 12px 0; +} + +/* Clear Audio is an actual action, not a bare text link. Match the Upload + primary/secondary action language while keeping destructive confirmation in + the modal itself. */ +:root[data-theme] + .upload-page.upload-page + .audio-clear + .img-footer + .chip.chip-clear { + min-height: 44px; + padding: 10px 18px; + border-radius: 12px; + font-weight: 800; + line-height: 1; + white-space: nowrap; + text-shadow: none; + box-shadow: 0 6px 18px rgba(138, 79, 8, 0.2); + transform: none; +} + +:root[data-theme="light"] + .upload-page.upload-page + .audio-clear + .img-footer + .chip.chip-clear { + background: #a16207; + border: 1px solid #a16207; + color: #ffffff; +} + +:root[data-theme="dark"] + .upload-page.upload-page + .audio-clear + .img-footer + .chip.chip-clear { + background: var(--tz-turmeric-soft, #f0c463); + border: 1px solid var(--tz-turmeric-soft, #f0c463); + color: #111827; +} + +:root[data-theme="light"] + .upload-page.upload-page + .audio-clear + .img-footer + .chip.chip-clear:hover:not(:disabled) { + background: #854d0e; + border-color: #854d0e; + color: #ffffff; + transform: translateY(-1px); +} + +:root[data-theme="dark"] + .upload-page.upload-page + .audio-clear + .img-footer + .chip.chip-clear:hover:not(:disabled) { + background: #f6d889; + border-color: #f6d889; + color: #111827; + transform: translateY(-1px); +} + +@media (max-width: 680px) { + :root[data-theme] + .fabric-search.fabric-search + .image-preview__media--with-actions + .image-preview__action-rail { + justify-content: center; + gap: 12px; + padding: 0; + } +} + +/* Final Search preview alignment: lift only the Clear action while preserving + the balanced Recrop position and all existing image/action behavior. */ +@media (min-width: 681px) { + :root[data-theme] + .fabric-search.fabric-search + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:first-child { + position: relative; + top: -28px; + } +} + +/* ============================================================ + Sticky image-search action visibility + ============================================================ + Recrop and New Image live in the fixed Search results toolbar. Give these + controls dedicated classes and a complete theme state so generic ghost / + outline rules cannot make them transparent or unreadable in dark mode. */ +:root[data-theme] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions { + opacity: 1; + visibility: visible; +} + +:root[data-theme] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 42px; + padding: 9px 16px; + border: 1px solid transparent; + border-radius: 999px; + opacity: 1; + visibility: visible; + filter: none; + backdrop-filter: none; + -webkit-backdrop-filter: none; + text-shadow: none; + transform: none; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn { + background: #a16207; + border-color: #a16207; + color: #ffffff; + box-shadow: 0 8px 20px rgba(138, 79, 8, 0.18); +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn { + background: var(--tz-turmeric-soft, #f0c463); + border-color: var(--tz-turmeric-soft, #f0c463); + color: #111827; + box-shadow: 0 8px 20px rgba(240, 196, 99, 0.2); +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn:hover:not(:disabled), +:root[data-theme="light"] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn:focus-visible { + background: #854d0e; + border-color: #854d0e; + color: #ffffff; + box-shadow: 0 10px 24px rgba(133, 77, 14, 0.24); + transform: translateY(-1px); +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn:hover:not(:disabled), +:root[data-theme="dark"] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn:focus-visible { + background: #f6d889; + border-color: #f6d889; + color: #111827; + box-shadow: 0 10px 24px rgba(240, 196, 99, 0.24); + transform: translateY(-1px); +} + +:root[data-theme] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn:focus-visible { + outline: 3px solid rgba(224, 168, 46, 0.34); + outline-offset: 2px; +} + +/* Final sticky image-action foreground guard. + These controls intentionally own their presentation instead of inheriting + generic ghost/outline button colors from route styles loaded later. */ +:root[data-theme="light"] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn { + color: #ffffff !important; + -webkit-text-fill-color: #ffffff !important; +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn { + color: #111827 !important; + -webkit-text-fill-color: #111827 !important; +} + +/* ============================================================ + Search crop dialog viewport containment + ============================================================ + Render the crop dialog through a document-level portal so transformed or + animated Search containers cannot redefine the containing block for its + fixed overlay. Keep the complete title, crop stage and actions visible in + the viewport; only the crop stage may scroll on unusually short screens. */ +.fabric-search-modal-root { + display: contents; +} + +:root[data-theme] + .fabric-search-modal-root.fabric-search + > .crop-drawer.crop-drawer { + position: fixed !important; + inset: 0 !important; + z-index: 2147483000; + box-sizing: border-box; + width: 100vw; + height: 100vh; + height: 100dvh; + min-height: 0; + padding: clamp(10px, 2vh, 20px) !important; + display: flex; + align-items: center !important; + justify-content: center !important; + overflow: hidden !important; + overscroll-behavior: contain; +} + +:root[data-theme] + .fabric-search-modal-root.fabric-search + > .crop-drawer.crop-drawer + > .crop-drawer__inner { + box-sizing: border-box; + width: min(720px, calc(100vw - 24px)) !important; + max-width: calc(100vw - 24px); + max-height: calc(100dvh - 20px) !important; + margin: 0 !important; + overflow: hidden !important; + display: flex; + flex-direction: column; +} + +:root[data-theme] .fabric-search-modal-root.fabric-search .crop-drawer__stage { + flex: 1 1 auto; + min-height: 0; + overflow: auto; + overscroll-behavior: contain; +} + +:root[data-theme] + .fabric-search-modal-root.fabric-search + :is(.crop-drawer__image-wrap, .crop-drawer__image) { + max-height: min(56dvh, calc(100dvh - 230px)) !important; +} + +:root[data-theme] + .fabric-search-modal-root.fabric-search + .crop-drawer__actions { + flex: 0 0 auto; +} + +@media (max-height: 680px) { + :root[data-theme] + .fabric-search-modal-root.fabric-search + > .crop-drawer.crop-drawer { + align-items: center !important; + padding: 8px !important; + } + + :root[data-theme] + .fabric-search-modal-root.fabric-search + > .crop-drawer.crop-drawer + > .crop-drawer__inner { + max-height: calc(100dvh - 16px) !important; + gap: 10px; + padding: 14px !important; + } + + :root[data-theme] + .fabric-search-modal-root.fabric-search + :is(.crop-drawer__image-wrap, .crop-drawer__image) { + max-height: calc(100dvh - 190px) !important; + } +} + + +/* ============================================================ + Unified user-facing connection errors + ============================================================ + Keep Search, Chat and List failures consistent and readable. The paired + foreground/background colors intentionally follow the requested theme rule: + white text in light mode and dark text in dark mode. */ +:root[data-theme] + :is( + .fabric-search.fabric-search .fabric-search__error, + .page-root .error[role="alert"], + .grid-page .grid-error[role="alert"] + ) { + box-sizing: border-box; + width: min(100%, 1180px); + min-height: 52px; + margin: 18px auto 0; + padding: 13px 20px; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + font-size: 0.98rem; + font-weight: 700; + line-height: 1.45; + border-radius: 10px; + text-wrap: balance; +} + +:root[data-theme="light"] + :is( + .fabric-search.fabric-search .fabric-search__error, + .page-root .error[role="alert"], + .grid-page .grid-error[role="alert"] + ) { + color: #ffffff !important; + -webkit-text-fill-color: #ffffff !important; + background: #b91c1c !important; + border: 1px solid #991b1b !important; + box-shadow: 0 10px 24px rgba(185, 28, 28, 0.22) !important; +} + +:root[data-theme="dark"] + :is( + .fabric-search.fabric-search .fabric-search__error, + .page-root .error[role="alert"], + .grid-page .grid-error[role="alert"] + ) { + color: #111827 !important; + -webkit-text-fill-color: #111827 !important; + background: #b91c1c !important; + border: 1px solid #991b1b !important; + box-shadow: 0 10px 24px rgba(185, 28, 28, 0.22) !important; +} + +:root[data-theme] + :is( + .fabric-search.fabric-search .fabric-search__error, + .page-root .error[role="alert"], + .grid-page .grid-error[role="alert"] + ) * { + color: inherit !important; + -webkit-text-fill-color: currentColor !important; +} + +@media (max-width: 680px) { + :root[data-theme] + :is( + .fabric-search.fabric-search .fabric-search__error, + .page-root .error[role="alert"], + .grid-page .grid-error[role="alert"] + ) { + width: calc(100% - 24px); + padding-inline: 14px; + font-size: 0.92rem; + } +} + +/* ============================================================ + Search crop dialog viewport recovery + ============================================================ + Keep the complete dialog reachable on short viewports. The modal remains + centered when it fits; when it is taller than the viewport it begins at + the top and the overlay becomes the scroll container. */ +:root[data-theme] + .fabric-search-modal-root.fabric-search + > .crop-drawer.crop-drawer { + width: auto !important; + height: auto !important; + max-height: none !important; + align-items: flex-start !important; + justify-content: center !important; + overflow-x: hidden !important; + overflow-y: auto !important; + overscroll-behavior: contain; + scrollbar-gutter: stable; + -webkit-overflow-scrolling: touch; + touch-action: pan-y; +} + +:root[data-theme] + .fabric-search-modal-root.fabric-search + > .crop-drawer.crop-drawer + > .crop-drawer__inner { + box-sizing: border-box; + width: min(720px, calc(100vw - 24px)) !important; + max-width: calc(100vw - 24px) !important; + height: auto !important; + min-height: 0 !important; + max-height: calc(100dvh - 20px) !important; + margin-block: auto !important; + margin-inline: 0 !important; + overflow: hidden !important; + flex: 0 1 auto; +} + +:root[data-theme] + .fabric-search-modal-root.fabric-search + .crop-drawer__stage { + flex: 1 1 auto; + height: auto !important; + min-height: 0 !important; + max-height: min(58dvh, calc(100dvh - 210px)) !important; + overflow: auto !important; + overscroll-behavior: contain; + scrollbar-gutter: stable; +} + +:root[data-theme] + .fabric-search-modal-root.fabric-search + :is(.crop-drawer__image-wrap, .crop-drawer__image) { + max-width: 100% !important; + height: auto !important; + min-height: 0 !important; + max-height: min(56dvh, calc(100dvh - 230px)) !important; +} + +:root[data-theme] + .fabric-search-modal-root.fabric-search + .crop-drawer__actions { + flex: 0 0 auto; +} + +@media (max-height: 680px) { + :root[data-theme] + .fabric-search-modal-root.fabric-search + > .crop-drawer.crop-drawer { + align-items: flex-start !important; + padding: 8px !important; + overflow-y: auto !important; + } + + :root[data-theme] + .fabric-search-modal-root.fabric-search + > .crop-drawer.crop-drawer + > .crop-drawer__inner { + height: auto !important; + min-height: 0 !important; + max-height: calc(100dvh - 16px) !important; + margin-block: 0 auto !important; + gap: 10px; + padding: 14px !important; + } + + :root[data-theme] + .fabric-search-modal-root.fabric-search + .crop-drawer__stage { + height: auto !important; + min-height: 0 !important; + max-height: calc(100dvh - 170px) !important; + } + + :root[data-theme] + .fabric-search-modal-root.fabric-search + :is(.crop-drawer__image-wrap, .crop-drawer__image) { + max-height: calc(100dvh - 190px) !important; + } +} \ No newline at end of file diff --git a/frontend/src/assets/styles/SeniorUiFixes.css.crop-modal-before b/frontend/src/assets/styles/SeniorUiFixes.css.crop-modal-before new file mode 100644 index 0000000..7d8ca21 --- /dev/null +++ b/frontend/src/assets/styles/SeniorUiFixes.css.crop-modal-before @@ -0,0 +1,2289 @@ +/* + * Scoped UI corrections requested during senior review. + * + * This file is imported after the existing theme/makeover layer so these + * narrowly targeted rules win without increasing selector priority across + * unrelated pages. + */ + +/* Shared foreground for accent-filled controls. Keep this token limited to + controls whose surface is also an accent colour. Applying it to neutral + cards/chips caused unreadable white-on-white and black-on-navy states. */ +:root { + --tz-accent-control-foreground: #171101; + --tz-filled-control-foreground: var(--tz-accent-control-foreground); + --tz-action-yellow-text: var(--tz-accent-control-foreground); + --tz-ui-accent-contrast: var(--tz-accent-control-foreground); + --tz-accent-control-surface: #e0a82e; + --tz-accent-control-surface-hover: #f0c463; +} + +:root[data-theme="light"] { + --tz-accent-control-foreground: #ffffff; + --tz-accent-control-surface: #8a4f08; + --tz-accent-control-surface-hover: #a45d0a; +} + +:root[data-theme="dark"] { + --tz-accent-control-foreground: #111827; + --tz-accent-control-surface: #e0a82e; + --tz-accent-control-surface-hover: #f0c463; +} + +/* Only true accent actions inherit the requested theme-dependent foreground. + Neutral/ghost controls and coloured information chips define their own + contrast-safe foregrounds below. */ +:root[data-theme] + :is( + .analysis-page .analysis-btn, + .analysis-page .description-wrapper .nav-btn, + .analysis-page .drawer-toggle-button, + .analysis-page .sample-gallery .analyze-button, + .app-wrapper--chat .page-root .chat-card-actions .btn-secondary, + .app-wrapper--chat .suggestion-chips .chip.primary, + .message-wrapper .inline-quick-replies .chip, + .fabric-search .btn--primary, + .fabric-search .category-picker__apply.btn.btn--primary, + .grid-page .grid-controls.inline button:not(:disabled), + .upload-page .btn.primary, + .upload-page .btn.submit, + .upload-page .name-field .cancel, + .composer-modal__button--confirm, + .composer .locked-control-btn--confirm, + .composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__button--confirm + ) { + color: var(--tz-accent-control-foreground); +} + +/* Light-mode white labels require a darker amber surface than the legacy + turmeric token. Set foreground and surface together for every accent-filled + control, including active navigation and search/category actions. */ +:root[data-theme="light"] + :is( + .header-nav a.active, + .analysis-page .analysis-btn, + .analysis-page .description-wrapper .nav-btn, + .analysis-page .drawer-toggle-button, + .analysis-page .sample-gallery .analyze-button, + .app-wrapper--chat .page-root .chat-card-actions .btn-secondary, + .app-wrapper--chat .suggestion-chips .chip.primary, + .message-wrapper .inline-quick-replies .chip, + .fabric-search .btn--primary, + .fabric-search .btn-primary, + .fabric-search .search-btn, + .fabric-search .category-picker__chip--active, + .fabric-search .category-picker__apply.btn.btn--primary, + .grid-page .grid-controls.inline button:not(:disabled), + .upload-page .btn.primary, + .upload-page .btn.submit, + .upload-page .name-field .cancel, + .composer-modal__button--confirm, + .composer .locked-control-btn--confirm, + .composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__button--confirm + ) { + background: linear-gradient( + 135deg, + var(--tz-accent-control-surface), + var(--tz-accent-control-surface-hover) + ); + border-color: var(--tz-accent-control-surface); + color: var(--tz-accent-control-foreground); + text-shadow: none; +} + +/* Analysis: keep each action pair above its own card and anchor the actions to + the card's outer corners. These rules support both the original and the + already-reordered component markup. */ +.analysis-page .image-preview-container > .preview-buttons-container, +.analysis-page .description-wrapper > .nav-buttons { + order: 1; + align-self: stretch; + width: 100%; + min-height: 44px; + margin: 0 0 16px; + display: flex; + flex-flow: row nowrap; + align-items: center; + justify-content: space-between; + gap: 16px; + transform: none; +} + +/* Short and Long remain a single left-aligned action group. Prev/Next keep + their separate corner-aligned result navigation below. */ +.analysis-page .image-preview-container > .preview-buttons-container { + justify-content: flex-start; +} + +.analysis-page .image-preview-container > .preview-visual, +.analysis-page .description-wrapper > .description-box { + order: 2; +} + +.analysis-page .image-preview-container > .filesize-name { + order: 3; +} + +.analysis-page .preview-buttons-container > .validation-text { + width: 100%; + margin: 0; + text-align: center; +} + +/* Do not let legacy centering/flex rules pull the corner controls inward. */ +.analysis-page .preview-buttons-container > .analysis-btn, +.analysis-page .description-wrapper > .nav-buttons > .nav-btn { + flex: 0 0 auto; + margin: 0; +} + +/* Chat: slightly reduce visual weight and vertical density. This keeps the + existing layout and interaction model while fitting more conversation in the + viewport and reducing unnecessary scrolling on laptop screens. */ +.app-wrapper--chat .page-root .chat-card-title .main { + font-size: 14px; + font-weight: 700; + line-height: 1.25; +} + +.app-wrapper--chat .page-root .chat-card-actions .btn { + font-size: 13px; + font-weight: 700; +} + +.app-wrapper--chat .page-root .empty-state-large h3 { + margin-bottom: 7px; + font-size: clamp(20px, 2vw, 24px); + font-weight: 700; + line-height: 1.25; + letter-spacing: -0.015em; +} + +.app-wrapper--chat .page-root .empty-state-large p { + margin-bottom: 16px; + font-size: 14px; + font-weight: 400; + line-height: 1.4; +} + +.app-wrapper--chat .page-root .empty-state-large .chip, +.app-wrapper--chat .page-root .empty-state-large .chip:nth-child(odd), +.app-wrapper--chat .page-root .empty-state-large .chip:nth-child(even) { + min-height: 42px; + padding: 8px 12px; + font-size: 13px; + font-weight: 650; + line-height: 1.25; +} + +.app-wrapper--chat .message-wrapper .msg-row { + margin-block: 6px; +} + +.app-wrapper--chat .assistant-block, +.app-wrapper--chat .bubble.user { + font-size: 14px; + font-weight: 400; + line-height: 1.45; +} + +.app-wrapper--chat .assistant-block--with-copy { + padding: 9px 44px 9px 12px; +} + +.app-wrapper--chat .assistant-block .assistant-content p { + margin-bottom: 0.5rem; +} + +.app-wrapper--chat .assistant-block ul, +.app-wrapper--chat .assistant-block ol { + margin-block: 6px 9px; + line-height: 1.45; +} + +.app-wrapper--chat .assistant-block li { + margin-block: 4px; + font-size: 14px; +} + +.app-wrapper--chat .assistant-block h1, +.app-wrapper--chat .assistant-block h2, +.app-wrapper--chat .assistant-block h3, +.app-wrapper--chat .assistant-block h4 { + margin: 0.65rem 0 0.35rem; + font-weight: 700; + line-height: 1.3; +} + +.app-wrapper--chat .assistant-block h1 { + font-size: 1.35rem; +} + +.app-wrapper--chat .assistant-block h2 { + font-size: 1.2rem; +} + +.app-wrapper--chat .assistant-block h3, +.app-wrapper--chat .assistant-block h4 { + font-size: 1.08rem; +} + +/* Attachment preview: center the Remove label geometrically inside its pill. + Explicit sizing prevents inherited global button line-height/padding rules + from nudging the text vertically. */ +.app-wrapper--chat .composer .upload-remove { + box-sizing: border-box; + min-height: 34px; + padding: 0 12px; + display: inline-flex; + align-items: center; + justify-content: center; + line-height: 1; + text-align: center; +} + +@media (max-width: 720px) { + .analysis-page .image-preview-container > .preview-buttons-container, + .analysis-page .description-wrapper > .nav-buttons { + gap: 10px; + margin-bottom: 12px; + } + + .analysis-page .preview-buttons-container > .analysis-btn, + .analysis-page .description-wrapper > .nav-buttons > .nav-btn { + flex: 0 1 48%; + min-width: 0; + padding-inline: 12px; + } + + .app-wrapper--chat .page-root .empty-state-large h3 { + font-size: 20px; + } +} + +/* Follow-up: legacy Analysis CSS still centers the result navigation in some + viewport/theme combinations. The repeated class raises specificity above + route-level centering rules even when that stylesheet loads later. */ +.analysis-page .description-wrapper > .nav-buttons.nav-buttons { + box-sizing: border-box; + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + width: 100%; + max-width: none; + padding: 0; + justify-content: stretch; + transform: none; +} + +.analysis-page + .description-wrapper + > .nav-buttons.nav-buttons + > .nav-btn:first-child { + justify-self: start; +} + +.analysis-page + .description-wrapper + > .nav-buttons.nav-buttons + > .nav-btn:last-child { + justify-self: end; +} + +/* Chat attachment removal confirmation: remove hard-coded light-theme inline + colours while preserving the existing state and click handlers. */ +.composer-modal-overlay { + position: fixed; + inset: 0; + z-index: 1300; + padding: 16px; + display: flex; + align-items: center; + justify-content: center; +} + +.composer-modal-overlay .composer-modal--remove-attachment { + --remove-dialog-surface: #ffffff; + --remove-dialog-text: #0f172a; + --remove-dialog-muted: #475569; + --remove-dialog-media: #f1f5f9; + --remove-dialog-border: rgba(15, 23, 42, 0.16); + --remove-dialog-media-border: rgba(15, 23, 42, 0.12); + --remove-dialog-cancel: #ffffff; + + box-sizing: border-box; + width: min(520px, calc(100vw - 32px)); + max-width: 100%; + padding: 18px; + border: 1px solid var(--remove-dialog-border); + border-radius: 16px; + background: var(--remove-dialog-surface); + color: var(--remove-dialog-text); + box-shadow: 0 20px 56px rgba(15, 23, 42, 0.28); +} + +:root[data-theme="dark"] + .composer-modal-overlay + .composer-modal--remove-attachment { + --remove-dialog-surface: #0f172a; + --remove-dialog-text: #f8fafc; + --remove-dialog-muted: #cbd5e1; + --remove-dialog-media: #1e293b; + --remove-dialog-border: rgba(226, 232, 240, 0.24); + --remove-dialog-media-border: rgba(226, 232, 240, 0.2); + --remove-dialog-cancel: #1e293b; + + box-shadow: 0 22px 64px rgba(0, 0, 0, 0.5); +} + +.composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__layout { + display: grid; + grid-template-columns: 84px minmax(0, 1fr); + gap: 14px; + align-items: center; +} + +.composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__media { + width: 84px; + height: 84px; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid var(--remove-dialog-media-border); + border-radius: 10px; + background: var(--remove-dialog-media); + color: var(--remove-dialog-muted); + font-size: 13px; + font-weight: 700; + line-height: 1; +} + +.composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__content { + min-width: 0; +} + +.composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__title { + margin: 0 0 6px; + color: var(--remove-dialog-text); + font-size: 16px; + font-weight: 800; + line-height: 1.3; +} + +.composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__filename { + color: var(--remove-dialog-muted); + font-size: 14px; + font-weight: 500; + line-height: 1.4; + overflow-wrap: anywhere; +} + +.composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__actions { + margin-top: 14px; + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__button { + min-height: 38px; + padding: 0 14px; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 9px; + font-size: 14px; + font-weight: 800; + line-height: 1; + cursor: pointer; +} + +.composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__button--confirm { + border: 1px solid var(--tz-turmeric, #e0a82e); + background: var(--tz-turmeric, #e0a82e); + color: #171101; +} + +.composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__button--cancel { + border: 1px solid var(--remove-dialog-border); + background: var(--remove-dialog-cancel); + color: var(--remove-dialog-text); +} + +@media (max-width: 520px) { + .composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__layout { + grid-template-columns: 1fr; + text-align: center; + } + + .composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__media { + justify-self: center; + } + + .composer-modal-overlay + .composer-modal--remove-attachment + .composer-remove-dialog__actions { + justify-content: center; + } +} + +/* ============================================================ + Theme contrast regression guard + ============================================================ + These rules repair the controls shown in the supplied screenshots and the + same semantic control families elsewhere. They intentionally set surface and + foreground together so a future text-only theme override cannot recreate the + contrast failures. */ + +/* Analysis sample cards are neutral controls, not accent buttons. */ +:root[data-theme="light"] + .analysis-page + .drawer-panel + .sample-gallery + .sample-card + .sample-card__select { + background: #0f172a; + color: #ffffff; + border-color: rgba(15, 23, 42, 0.18); +} + +:root[data-theme="dark"] + .analysis-page + .drawer-panel + .sample-gallery + .sample-card + .sample-card__select { + background: #f8fafc; + color: #111827; + border-color: rgba(248, 250, 252, 0.92); +} + +:root[data-theme] + .analysis-page + .drawer-panel + .sample-gallery + .sample-card.sample-card:hover + .sample-card__select, +:root[data-theme] + .analysis-page + .drawer-panel + .sample-gallery + .sample-card.sample-card:focus-visible + .sample-card__select { + background: linear-gradient(135deg, #e0a82e, #f0c463); + color: #171101; + border-color: #f0c463; +} + +/* Chat landing prompts: pair each foreground with a deliberate surface. + Light mode uses strong coloured boxes with white labels; dark mode uses + pastel boxes with black labels, matching the requested theme direction. */ +:root[data-theme="light"] + .app-wrapper--chat + .page-root + .empty-state-large + .chip:nth-child(1) { + background: #9a3412; + border-color: #c2410c; + color: #ffffff; +} + +:root[data-theme="light"] + .app-wrapper--chat + .page-root + .empty-state-large + .chip:nth-child(2) { + background: #4338ca; + border-color: #4f46e5; + color: #ffffff; +} + +:root[data-theme="light"] + .app-wrapper--chat + .page-root + .empty-state-large + .chip:nth-child(3) { + background: #047857; + border-color: #059669; + color: #ffffff; +} + +:root[data-theme="light"] + .app-wrapper--chat + .page-root + .empty-state-large + .chip:nth-child(4) { + background: #be185d; + border-color: #db2777; + color: #ffffff; +} + +:root[data-theme="dark"] + .app-wrapper--chat + .page-root + .empty-state-large + .chip:nth-child(1) { + background: #fed7aa; + border-color: #fb923c; + color: #111827; +} + +:root[data-theme="dark"] + .app-wrapper--chat + .page-root + .empty-state-large + .chip:nth-child(2) { + background: #c7d2fe; + border-color: #818cf8; + color: #111827; +} + +:root[data-theme="dark"] + .app-wrapper--chat + .page-root + .empty-state-large + .chip:nth-child(3) { + background: #a7f3d0; + border-color: #34d399; + color: #111827; +} + +:root[data-theme="dark"] + .app-wrapper--chat + .page-root + .empty-state-large + .chip:nth-child(4) { + background: #fbcfe8; + border-color: #f472b6; + color: #111827; +} + +:root[data-theme] + .app-wrapper--chat + .page-root + .empty-state-large + .chip:disabled { + color: inherit; + opacity: 0.62; +} + +/* Assistant replies now follow the active theme instead of remaining a white + card in dark mode. Markdown content inherits the same readable foreground. */ +:root[data-theme="light"] .app-wrapper--chat .assistant-block { + background: #ffffff; + color: #0f172a; + border-color: rgba(15, 23, 42, 0.14); +} + +:root[data-theme="dark"] .app-wrapper--chat .assistant-block { + background: #171f33; + color: #f8fafc; + border-color: rgba(226, 232, 240, 0.22); +} + +:root[data-theme] + .app-wrapper--chat + .assistant-block + :is(.assistant-content, p, li, h1, h2, h3, h4, strong) { + color: inherit; +} + +:root[data-theme="light"] + .app-wrapper--chat + .assistant-block--with-copy + .copy-btn { + background: #f1f5f9; + color: #0f172a; + border-color: rgba(15, 23, 42, 0.14); +} + +:root[data-theme="dark"] + .app-wrapper--chat + .assistant-block--with-copy + .copy-btn { + background: #24304d; + color: #f8fafc; + border-color: rgba(226, 232, 240, 0.22); +} + +/* Upload primary action had black text on a translucent purple surface in dark + mode. Use the accent surface whenever the accent foreground token is used. */ +:root[data-theme="light"] .upload-page .btn.primary { + background: var(--tz-accent-control-surface, #8a4f08); + border-color: var(--tz-accent-control-surface, #8a4f08); + color: #ffffff; +} + +:root[data-theme="dark"] .upload-page .btn.primary { + background: var(--tz-turmeric, #e0a82e); + border-color: var(--tz-turmeric, #e0a82e); + color: #111827; +} + +:root[data-theme="light"] .upload-page .btn.submit, +:root[data-theme="light"] .upload-page .name-field .cancel { + background: var(--tz-accent-control-surface, #8a4f08); + border-color: var(--tz-accent-control-surface, #8a4f08); + color: #ffffff; +} + +:root[data-theme="dark"] .upload-page .btn.submit { + background: #38bdf8; + border-color: #38bdf8; + color: #111827; +} + +:root[data-theme="dark"] .upload-page .name-field .cancel { + background: #f8fafc; + border: 1px solid rgba(248, 250, 252, 0.86); + color: #111827; +} + +:root[data-theme="dark"] .upload-page .btn:not(.primary):not(.submit), +:root[data-theme="dark"] .upload-page .confirm-actions .btn-cancel { + background: #1e293b; + color: #f8fafc; + border-color: rgba(226, 232, 240, 0.24); +} + +:root[data-theme="light"] .upload-page .btn:not(.primary):not(.submit), +:root[data-theme="light"] .upload-page .confirm-actions .btn-cancel { + background: #ffffff; + color: #0f172a; + border-color: rgba(15, 23, 42, 0.16); +} + +/* Confirmation dialogs and locked composer controls use the same semantic + accent/neutral split in both themes. */ +:root[data-theme="light"] .composer-modal--confirm-upload, +:root[data-theme="light"] .upload-page .confirm-modal { + background: #ffffff; + color: #0f172a; + border-color: rgba(15, 23, 42, 0.16); +} + +:root[data-theme="dark"] .composer-modal--confirm-upload, +:root[data-theme="dark"] .upload-page .confirm-modal { + background: #0f172a; + color: #f8fafc; + border-color: rgba(226, 232, 240, 0.24); +} + +:root[data-theme] + .composer-modal--confirm-upload + :is(.composer-modal__title, .composer-modal__meta, .composer-modal__hint) { + color: inherit; +} + +:root[data-theme="light"] + .composer-modal--confirm-upload + .composer-modal__button--confirm, +:root[data-theme="light"] .composer .locked-control-btn--confirm { + background: var(--tz-accent-control-surface, #8a4f08); + border-color: var(--tz-accent-control-surface, #8a4f08); + color: #ffffff; +} + +:root[data-theme="dark"] + .composer-modal--confirm-upload + .composer-modal__button--confirm, +:root[data-theme="dark"] .composer .locked-control-btn--confirm { + background: var(--tz-turmeric, #e0a82e); + border-color: var(--tz-turmeric, #e0a82e); + color: #111827; +} + +:root[data-theme="light"] + .composer-modal--confirm-upload + .composer-modal__button--cancel, +:root[data-theme="light"] .composer .locked-control-btn--cancel { + background: #f8fafc; + color: #0f172a; + border-color: rgba(15, 23, 42, 0.16); +} + +:root[data-theme="dark"] + .composer-modal--confirm-upload + .composer-modal__button--cancel, +:root[data-theme="dark"] .composer .locked-control-btn--cancel { + background: #1e293b; + color: #f8fafc; + border-color: rgba(226, 232, 240, 0.24); +} + +/* ============================================================ + Analysis response position and Upload action hierarchy + ============================================================ */ + +/* Keep the response position beside the copy action in the lower-right corner + of the result card. The value is derived from the existing response history, + so Prev/Next behavior and regeneration logic remain unchanged. */ +.analysis-page .description-wrapper .response-copy-only { + justify-content: flex-end; + gap: 10px; +} + +.analysis-page .description-wrapper .response-copy-only .response-position { + min-width: 46px; + min-height: 34px; + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0 10px; + border: 1px solid rgba(255, 255, 255, 0.28); + border-radius: 999px; + background: rgba(15, 23, 42, 0.72); + color: #f8fafc; + font-size: 0.875rem; + font-weight: 800; + line-height: 1; + letter-spacing: 0.02em; + font-variant-numeric: tabular-nums; +} + +.analysis-page .description-wrapper .response-copy-only .copy-btn { + margin-left: 0; +} + +/* Submit is the primary Upload action; Cancel is deliberately secondary. + Surface and foreground are always paired to avoid theme contrast regressions. */ +:root[data-theme="light"] .upload-page .name-field .btn.submit { + background: var(--tz-accent-control-surface, #8a4f08); + border-color: var(--tz-accent-control-surface, #8a4f08); + color: #ffffff; + box-shadow: 0 6px 18px rgba(138, 79, 8, 0.22); +} + +:root[data-theme="light"] .upload-page .name-field .cancel { + background: #ffffff; + border: 1px solid rgba(138, 79, 8, 0.48); + color: #7c4707; + box-shadow: 0 4px 14px rgba(15, 23, 42, 0.08); +} + +:root[data-theme="dark"] .upload-page .name-field .btn.submit { + background: var(--tz-turmeric, #e0a82e); + border-color: var(--tz-turmeric, #e0a82e); + color: #111827; + box-shadow: 0 6px 20px rgba(224, 168, 46, 0.22); +} + +:root[data-theme="dark"] .upload-page .name-field .cancel { + background: #1e293b; + border: 1px solid rgba(224, 168, 46, 0.48); + color: #f8fafc; + box-shadow: 0 4px 14px rgba(0, 0, 0, 0.2); +} + +:root[data-theme="light"] + .upload-page + .name-field + .btn.submit:hover:not(:disabled) { + background: var(--tz-accent-control-surface-hover, #a45d0a); + border-color: var(--tz-accent-control-surface-hover, #a45d0a); +} + +:root[data-theme="light"] + .upload-page + .name-field + .cancel:hover:not(:disabled) { + background: #fff7ed; + border-color: #8a4f08; + color: #6b3b05; +} + +:root[data-theme="dark"] + .upload-page + .name-field + .btn.submit:hover:not(:disabled) { + background: var(--tz-accent-control-surface-hover, #f0c463); + border-color: var(--tz-accent-control-surface-hover, #f0c463); +} + +:root[data-theme="dark"] .upload-page .name-field .cancel:hover:not(:disabled) { + background: #293548; + border-color: rgba(240, 196, 99, 0.72); + color: #ffffff; +} + +:root[data-theme] .upload-page .name-field .btn.submit:disabled { + opacity: 0.55; + box-shadow: none; +} + +/* ============================================================ + Requested cross-page control alignment and theme visibility + ============================================================ + Keep the latest review changes semantic and narrowly scoped. White-on-gold + actions use a deeper accessible gold; selected categories use a theme-aware + foreground so dark mode can retain dark ink on the brighter yellow. */ +:root { + --tz-white-on-gold-surface: #a65f00; + --tz-white-on-gold-surface-hover: #8a4f08; +} + +:root[data-theme="light"] { + --tz-selected-category-surface: #a65f00; + --tz-selected-category-foreground: #ffffff; +} + +:root[data-theme="dark"] { + --tz-selected-category-surface: var(--tz-turmeric, #e0a82e); + --tz-selected-category-foreground: #111827; +} + +/* Search image workspace: pin Clear Search and Recrop to the requested outer + corners of the Original Image frame, independent of image dimensions. */ +.fabric-search.fabric-search .image-preview__frame { + position: relative; +} + +.fabric-search.fabric-search .image-preview__frame-actions--top { + inset: 14px 14px auto auto; + margin: 0; +} + +.fabric-search.fabric-search .image-preview__frame-actions--bottom { + inset: auto 14px 14px auto; + margin: 0; +} + +:root[data-theme] + .fabric-search.fabric-search + .image-preview__frame-actions + .btn, +:root[data-theme] + .fabric-search.fabric-search + .search-bar__image-actions + :is(.btn--outline, .btn--ghost) { + background: var(--tz-white-on-gold-surface); + border-color: var(--tz-white-on-gold-surface); + color: #ffffff; + box-shadow: 0 10px 24px rgba(138, 79, 8, 0.24); + text-shadow: none; +} + +:root[data-theme] + .fabric-search.fabric-search + .image-preview__frame-actions + .btn:hover:not(:disabled), +:root[data-theme] + .fabric-search.fabric-search + .search-bar__image-actions + :is(.btn--outline, .btn--ghost):hover:not(:disabled) { + background: var(--tz-white-on-gold-surface-hover); + border-color: var(--tz-white-on-gold-surface-hover); + color: #ffffff; +} + +/* Selected Search categories: light theme uses white labels; dark theme uses + dark labels on the brighter yellow requested during review. */ +:root[data-theme] + .fabric-search.fabric-search + .category-picker__chip.category-picker__chip--active, +:root[data-theme] + .fabric-search.fabric-search + .category-picker__chip.category-picker__chip--active:hover { + background: var(--tz-selected-category-surface); + border-color: var(--tz-selected-category-surface); + color: var(--tz-selected-category-foreground); + box-shadow: 0 10px 24px rgba(166, 95, 0, 0.2); + text-shadow: none; +} + +:root[data-theme] + .fabric-search.fabric-search + .category-picker__chip.category-picker__chip--active + :is( + .category-picker__chip-check, + .category-picker__chip-icon, + .category-picker__chip-label + ) { + color: inherit; + opacity: 1; +} + +/* Search crop confirmation is intentionally white-on-gold in both themes. */ +:root[data-theme] + .fabric-search.fabric-search + .crop-drawer__actions + .btn.btn--primary { + background: var(--tz-white-on-gold-surface); + border-color: var(--tz-white-on-gold-surface); + color: #ffffff; + box-shadow: 0 10px 24px rgba(138, 79, 8, 0.24); +} + +:root[data-theme] + .fabric-search.fabric-search + .crop-drawer__actions + .btn.btn--primary:hover:not(:disabled) { + background: var(--tz-white-on-gold-surface-hover); + border-color: var(--tz-white-on-gold-surface-hover); + color: #ffffff; +} + +/* Upload dark theme: Submit and Cancel now share the exact Start Recording + surface and foreground while retaining their existing handlers/states. */ +:root[data-theme="dark"] + .upload-page.upload-page + :is( + .rec-controls .btn.primary, + .name-field .btn.submit, + .name-field .cancel + ) { + background: linear-gradient( + 135deg, + var(--tz-turmeric, #e0a82e), + var(--tz-turmeric-soft, #f0c463) + ); + border-color: var(--tz-turmeric, #e0a82e); + color: #111827; + box-shadow: 0 8px 20px rgba(224, 168, 46, 0.22); + text-shadow: none; +} + +:root[data-theme="dark"] + .upload-page.upload-page + :is( + .rec-controls .btn.primary, + .name-field .btn.submit, + .name-field .cancel + ):hover:not(:disabled) { + background: var(--tz-turmeric-soft, #f0c463); + border-color: var(--tz-turmeric-soft, #f0c463); + color: #111827; +} + +/* Analysis sample-image selection: light-theme hover/focus uses the requested + white label on a yellow surface. */ +:root[data-theme="light"] + .analysis-page.analysis-page + .drawer-panel + .sample-gallery + .sample-card.sample-card:hover + .sample-card__select, +:root[data-theme="light"] + .analysis-page.analysis-page + .drawer-panel + .sample-gallery + .sample-card.sample-card:focus-visible + .sample-card__select { + background: var(--tz-white-on-gold-surface); + border-color: var(--tz-white-on-gold-surface); + color: #ffffff; +} + +/* Analysis image-removal confirmation: both requested light-theme actions are + white-on-gold, with the dialog surface itself left unchanged. */ +:root[data-theme="light"] + .analysis-page.analysis-page + .confirm-actions + :is(.btn.btn-cancel, .btn.btn-confirm) { + background: var(--tz-white-on-gold-surface); + border-color: var(--tz-white-on-gold-surface); + color: #ffffff; + box-shadow: 0 6px 16px rgba(138, 79, 8, 0.2); +} + +:root[data-theme="light"] + .analysis-page.analysis-page + .confirm-actions + :is(.btn.btn-cancel, .btn.btn-confirm):hover:not(:disabled) { + background: var(--tz-white-on-gold-surface-hover); + border-color: var(--tz-white-on-gold-surface-hover); + color: #ffffff; +} + +/* Analysis header/navigation was inheriting muted route colours. Restore the + same strong, theme-aware typography used by the other application pages. */ +:root[data-theme="light"] + .app-wrapper--analysis + .site-header + :is(.brand-name, .header-nav a:not(.active)) { + color: #1f2937; + font-weight: 750; + opacity: 1; +} + +:root[data-theme="dark"] + .app-wrapper--analysis + .site-header + :is(.brand-name, .header-nav a:not(.active)) { + color: #f1f5f9; + font-weight: 750; + opacity: 1; +} + +:root[data-theme="light"] + .app-wrapper--analysis + .site-header + .header-nav + a.active { + background: var(--tz-white-on-gold-surface); + border-color: var(--tz-white-on-gold-surface); + color: #ffffff; + font-weight: 800; + opacity: 1; +} + +:root[data-theme="dark"] + .app-wrapper--analysis + .site-header + .header-nav + a.active { + background: var(--tz-turmeric, #e0a82e); + border-color: var(--tz-turmeric, #e0a82e); + color: #111827; + font-weight: 800; + opacity: 1; +} + +:root[data-theme="light"] + .app-wrapper--analysis + .analysis-page + :is(.animated-header h1, .top-texts) { + color: #0f172a; + opacity: 1; +} + +:root[data-theme="dark"] + .app-wrapper--analysis + .analysis-page + :is(.animated-header h1, .top-texts) { + color: #f8fafc; + opacity: 1; +} + +/* Chat sample prompts remain distinguishable but use balanced, less muddy + colours in both themes. */ +:root[data-theme="light"] + .app-wrapper--chat + .page-root + .empty-state-large + .chips + > .chip:nth-child(1) { + background: #b45309; + border-color: #d97706; + color: #ffffff; +} + +:root[data-theme="light"] + .app-wrapper--chat + .page-root + .empty-state-large + .chips + > .chip:nth-child(2) { + background: #4f46e5; + border-color: #6366f1; + color: #ffffff; +} + +:root[data-theme="light"] + .app-wrapper--chat + .page-root + .empty-state-large + .chips + > .chip:nth-child(3) { + background: #0f766e; + border-color: #0d9488; + color: #ffffff; +} + +:root[data-theme="light"] + .app-wrapper--chat + .page-root + .empty-state-large + .chips + > .chip:nth-child(4) { + background: #be185d; + border-color: #db2777; + color: #ffffff; +} + +:root[data-theme="dark"] + .app-wrapper--chat + .page-root + .empty-state-large + .chips + > .chip:nth-child(1) { + background: #fdba74; + border-color: #fb923c; + color: #111827; +} + +:root[data-theme="dark"] + .app-wrapper--chat + .page-root + .empty-state-large + .chips + > .chip:nth-child(2) { + background: #a5b4fc; + border-color: #818cf8; + color: #111827; +} + +:root[data-theme="dark"] + .app-wrapper--chat + .page-root + .empty-state-large + .chips + > .chip:nth-child(3) { + background: #6ee7b7; + border-color: #34d399; + color: #111827; +} + +:root[data-theme="dark"] + .app-wrapper--chat + .page-root + .empty-state-large + .chips + > .chip:nth-child(4) { + background: #f9a8d4; + border-color: #f472b6; + color: #111827; +} + +:root[data-theme] + .app-wrapper--chat + .page-root + .empty-state-large + .chips + > .chip:hover:not(:disabled) { + filter: brightness(1.06); + transform: translateY(-1px); +} + +/* ============================================================ + Search preview structure and compact category action follow-up + ============================================================ + Keep the title and its actions inside each image frame so their alignment is + independent of the uploaded image's aspect ratio. These rules are scoped to + the Search preview and do not affect result cards or the sticky toolbar. */ +:root[data-theme] .fabric-search.fabric-search .category-picker__toggle-all { + min-height: 32px; + height: 32px; + padding: 0 14px; + line-height: 1; +} + +:root[data-theme] .fabric-search.fabric-search .image-preview__frame { + display: flex; + flex-direction: column; + align-items: stretch; + justify-items: initial; +} + +:root[data-theme] .fabric-search.fabric-search .image-preview__frame-header { + display: grid; + grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); + align-items: center; + flex: 0 0 auto; + min-height: 50px; + padding: 8px 14px; + border-bottom: 1px solid var(--tz-ui-border, rgba(100, 116, 139, 0.22)); + background: transparent; +} + +:root[data-theme] .fabric-search.fabric-search .image-preview__pane-title { + grid-column: 2; + justify-self: center; + align-self: center; + display: block; + min-height: 0; + margin: 0; + padding: 0; + border: 0; + border-radius: 0; + background: transparent; + box-shadow: none; + color: var(--tz-ui-text, #f8fafc); + font-family: var(--font-sans, Inter, system-ui, sans-serif); + font-size: 13px; + font-weight: 900; + line-height: 1.2; + letter-spacing: 0.08em; + text-align: center; + text-shadow: none; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .image-preview__pane-title { + color: #0f172a; +} + +:root[data-theme] + .fabric-search.fabric-search + .image-preview__frame-header + .image-preview__frame-actions--top { + position: static; + grid-column: 3; + justify-self: end; + inset: auto; + margin: 0; +} + +:root[data-theme] .fabric-search.fabric-search .image-preview__media { + position: relative; + display: grid; + place-items: center; + flex: 1 1 auto; + min-height: 210px; + overflow: hidden; +} + +:root[data-theme] + .fabric-search.fabric-search + .image-preview__media + .image-preview__img { + height: min(410px, calc(52vh - 50px)); + max-height: min(410px, calc(52vh - 50px)); +} + +:root[data-theme] + .fabric-search.fabric-search + .image-preview__media + .image-preview__frame-actions--bottom { + position: absolute; + inset: auto 14px 14px auto; + margin: 0; +} + +/* Light mode uses a deeper accessible gold because the requested white label + does not meet contrast on the brighter dark-theme yellow. */ +:root[data-theme="light"] + .fabric-search.fabric-search + .crop-drawer__actions + .btn.btn--primary { + background: #a16207; + border-color: #a16207; + color: #ffffff; + text-shadow: none; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .crop-drawer__actions + .btn.btn--primary + span { + color: inherit; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .crop-drawer__actions + .btn.btn--primary:hover:not(:disabled) { + background: #854d0e; + border-color: #854d0e; + color: #ffffff; +} + +/* Clear/Recrop retain the project's theme convention: white on accessible gold + in light mode, dark ink on bright yellow in dark mode. */ +:root[data-theme="light"] + .fabric-search.fabric-search + .image-preview__frame-actions + .btn { + background: #a16207; + border-color: #a16207; + color: #ffffff; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .image-preview__frame-actions + .btn:hover:not(:disabled) { + background: #854d0e; + border-color: #854d0e; + color: #ffffff; +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .image-preview__frame-actions + .btn { + background: var(--tz-turmeric-soft, #f0c463); + border-color: var(--tz-turmeric-soft, #f0c463); + color: #111827; + text-shadow: none; +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .image-preview__frame-actions + .btn:hover:not(:disabled) { + background: #f6d889; + border-color: #f6d889; + color: #111827; +} + +@media (max-height: 700px) { + :root[data-theme] + .fabric-search.fabric-search + .image-preview__media + .image-preview__img { + height: calc(42vh - 50px); + max-height: calc(42vh - 50px); + } +} + +/* ============================================================ + Search render stability, text contrast, pagination, and shared + destructive-confirmation dialog follow-up + ============================================================ */ + +/* The hero upload control previously relied on hover styles to establish its + visible surface. Give it a complete non-hover state and avoid backdrop/GPU + effects that can leave the wide control partially unpainted until pointer + movement on some Windows/Chromium configurations. */ +:root[data-theme] + .fabric-search.fabric-search + .hero + .hero__search-controls + .hero__upload-btn.btn.btn--ghost { + box-sizing: border-box; + display: inline-flex; + align-items: center; + justify-content: center; + width: 100%; + min-height: 56px; + opacity: 1; + visibility: visible; + transform: none; + filter: none; + backdrop-filter: none; + -webkit-backdrop-filter: none; + isolation: isolate; + box-shadow: none; + transition: + background-color 160ms ease, + border-color 160ms ease, + color 160ms ease, + box-shadow 160ms ease; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .hero + .hero__search-controls + .hero__upload-btn.btn.btn--ghost { + background: #ffffff; + border: 1px solid rgba(138, 79, 8, 0.42); + color: #172033; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .hero + .hero__search-controls + .hero__upload-btn.btn.btn--ghost:hover:not(:disabled) { + background: #fffaf0; + border-color: #a16207; + color: #172033; + box-shadow: 0 8px 22px rgba(138, 79, 8, 0.12); + transform: none; +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .hero + .hero__search-controls + .hero__upload-btn.btn.btn--ghost { + background: #111827; + border: 1px solid rgba(240, 196, 99, 0.48); + color: #f8fafc; +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .hero + .hero__search-controls + .hero__upload-btn.btn.btn--ghost:hover:not(:disabled) { + background: #172033; + border-color: #f0c463; + color: #ffffff; + box-shadow: 0 8px 22px rgba(224, 168, 46, 0.14); + transform: none; +} + +/* Apply Filter follows the requested light-theme white-on-gold treatment. + Explicit child colours prevent older compact-toolbar rules from restoring + black text after lazy stylesheet loading. */ +:root[data-theme="light"] + .fabric-search.fabric-search + .category-picker.category-picker--compact + .category-picker__apply.btn.btn--primary { + background: linear-gradient(135deg, #a16207 0%, #b45309 100%); + border-color: #a16207; + color: #ffffff; + text-shadow: none; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .category-picker.category-picker--compact + .category-picker__apply.btn.btn--primary + :is(.category-picker__apply-label, .category-picker__apply-arrow) { + color: #ffffff; + text-shadow: none; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .category-picker.category-picker--compact + .category-picker__apply.btn.btn--primary:hover:not(:disabled) { + background: linear-gradient(135deg, #854d0e 0%, #a16207 100%); + border-color: #854d0e; + color: #ffffff; +} + +/* Search pagination should read as navigation text, not as another pair of + pill buttons. Keep the controls transparent and borderless while preserving + keyboard focus and clear enabled/disabled states in both themes. */ +:root[data-theme] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn { + min-height: 0; + padding: 6px 8px; + border: 0; + border-radius: 6px; + background: transparent; + box-shadow: none; + backdrop-filter: none; + -webkit-backdrop-filter: none; + text-shadow: none; + transform: none; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn { + color: #334155; +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn { + color: #f1f5f9; +} + +:root[data-theme] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:not(:disabled):hover { + border: 0; + background: transparent; + color: #b7791f; + box-shadow: none; + transform: none; +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:not(:disabled):hover { + color: #f0c463; +} + +:root[data-theme] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:disabled { + background: transparent; + border: 0; + box-shadow: none; + opacity: 0.42; +} + +:root[data-theme] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:focus-visible { + outline: 2px solid #e0a82e; + outline-offset: 2px; +} + +/* Upload and Analysis use the same destructive-confirmation language. A + neutral charcoal surface avoids the previous blue-vs-black inconsistency; + destructive intent remains red and Cancel remains neutral in both themes. */ +:root[data-theme] + :is( + .analysis-page.analysis-page .image-preview-container, + .upload-page.upload-page + ) + .confirm-overlay { + background: rgba(2, 6, 23, 0.72); + backdrop-filter: blur(3px); + -webkit-backdrop-filter: blur(3px); +} + +:root[data-theme] + :is( + .analysis-page.analysis-page .image-preview-container, + .upload-page.upload-page + ) + .confirm-modal { + width: min(420px, calc(100vw - 40px)); + max-width: 420px; + padding: 20px; + border: 1px solid rgba(148, 163, 184, 0.28); + border-radius: 12px; + background: #111827; + color: #f8fafc; + box-shadow: 0 18px 48px rgba(2, 6, 23, 0.5); +} + +:root[data-theme] + :is( + .analysis-page.analysis-page .image-preview-container, + .upload-page.upload-page + ) + :is(.confirm-title, .confirm-body) { + color: #f8fafc; + opacity: 1; + text-shadow: none; +} + +:root[data-theme] + :is( + .analysis-page.analysis-page .image-preview-container, + .upload-page.upload-page + ) + .confirm-actions + .btn.btn-cancel { + background: #374151; + border: 1px solid #4b5563; + color: #ffffff; + box-shadow: none; +} + +:root[data-theme] + :is( + .analysis-page.analysis-page .image-preview-container, + .upload-page.upload-page + ) + .confirm-actions + .btn.btn-confirm { + background: #dc2626; + border: 1px solid #dc2626; + color: #ffffff; + box-shadow: none; +} + +:root[data-theme] + :is( + .analysis-page.analysis-page .image-preview-container, + .upload-page.upload-page + ) + .confirm-actions + .btn.btn-cancel:hover:not(:disabled) { + background: #4b5563; + border-color: #64748b; + color: #ffffff; +} + +:root[data-theme] + :is( + .analysis-page.analysis-page .image-preview-container, + .upload-page.upload-page + ) + .confirm-actions + .btn.btn-confirm:hover:not(:disabled) { + background: #b91c1c; + border-color: #b91c1c; + color: #ffffff; +} + +/* ============================================================ + Search pagination, preview action spacing, and upload dialog centering + ============================================================ + These follow-up rules deliberately override the earlier transparent + pagination treatment and the absolute preview actions. They are limited to + the Search result controls and the Upload confirmation overlay. */ + +/* Prev/Next use the same filled accent language as the other Search actions. + The foreground follows the requested theme convention: white in light mode, + dark ink in dark mode. */ +:root[data-theme] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn { + min-height: 38px; + padding: 8px 16px; + border: 1px solid transparent; + border-radius: 999px; + box-shadow: 0 8px 20px rgba(138, 79, 8, 0.18); + backdrop-filter: none; + -webkit-backdrop-filter: none; + text-shadow: none; + transform: none; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn { + background: #a16207; + border-color: #a16207; + color: #ffffff; +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn { + background: var(--tz-turmeric-soft, #f0c463); + border-color: var(--tz-turmeric-soft, #f0c463); + color: #111827; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:not(:disabled):hover { + background: #854d0e; + border-color: #854d0e; + color: #ffffff; + box-shadow: 0 10px 24px rgba(133, 77, 14, 0.24); + transform: translateY(-1px); +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:not(:disabled):hover { + background: #f6d889; + border-color: #f6d889; + color: #111827; + box-shadow: 0 10px 24px rgba(240, 196, 99, 0.2); + transform: translateY(-1px); +} + +:root[data-theme] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:disabled { + border-color: currentColor; + box-shadow: none; + opacity: 0.5; + transform: none; +} + +/* Reserve a real action rail beside the Original Image instead of absolutely + pinning controls against the frame edge. This keeps Clear slightly below the + title, lifts Recrop away from the clipped lower edge, and remains stable for + tall, square, and landscape source images. */ +:root[data-theme] + .fabric-search.fabric-search + .image-preview__media--with-actions { + grid-template-columns: minmax(0, 1fr) minmax(112px, 132px); + gap: 12px; + padding: 12px 14px; + overflow: hidden; +} + +:root[data-theme] + .fabric-search.fabric-search + .image-preview__media--with-actions + .image-preview__img { + grid-column: 1; + width: 100%; + max-width: 100%; + min-width: 0; + object-fit: contain; +} + +:root[data-theme] + .fabric-search.fabric-search + .image-preview__media--with-actions + .image-preview__action-rail { + position: static; + inset: auto; + grid-column: 2; + align-self: stretch; + display: flex; + flex-direction: column; + align-items: stretch; + justify-content: space-evenly; + gap: 18px; + min-width: 0; + margin: 0; + padding: 16px 0; + z-index: 3; +} + +:root[data-theme] + .fabric-search.fabric-search + .image-preview__action-rail + .btn { + width: 100%; + min-width: 0; + justify-content: center; + white-space: nowrap; +} + +@media (max-width: 680px) { + :root[data-theme] + .fabric-search.fabric-search + .image-preview__media--with-actions { + grid-template-columns: minmax(0, 1fr); + } + + :root[data-theme] + .fabric-search.fabric-search + .image-preview__media--with-actions + .image-preview__action-rail { + grid-column: 1; + flex-direction: row; + justify-content: flex-end; + padding: 0; + } + + :root[data-theme] + .fabric-search.fabric-search + .image-preview__action-rail + .btn { + width: auto; + min-width: 104px; + } +} + +/* Keep Upload remove confirmations centered in the visible viewport even when + the page is scrolled. Dynamic viewport units avoid the dialog drifting low + when browser chrome changes the available height. */ +:root[data-theme] .upload-page.upload-page > .confirm-overlay { + position: fixed; + inset: 0; + width: 100vw; + min-width: 100vw; + height: 100vh; + min-height: 100vh; + height: 100dvh; + min-height: 100dvh; + display: grid; + place-items: center; + align-content: center; + justify-content: center; + margin: 0; + padding: 24px; + overflow: auto; + z-index: 10000; +} + +:root[data-theme] .upload-page.upload-page > .confirm-overlay > .confirm-modal { + position: relative; + inset: auto; + margin: 0; + max-height: calc(100dvh - 48px); + overflow: auto; + transform: none; +} + +/* ============================================================ + Search pagination persistence, preview action balance, and Clear Audio + ============================================================ + Keep disabled pagination controls visible, balance the Original Image + actions in a stable rail, and give Clear Audio the same visual hierarchy as + the Upload form actions. These rules are intentionally page-scoped. */ + +/* Both pagination controls remain visible on every page. Disabled state is + communicated through cursor and a softer surface—not by disappearing. */ +:root[data-theme="light"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn, +:root[data-theme="light"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:disabled, +:root[data-theme="light"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:disabled:hover { + background: #a16207; + border-color: #a16207; + color: #ffffff; + opacity: 1; + visibility: visible; + box-shadow: 0 8px 20px rgba(138, 79, 8, 0.18); +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn, +:root[data-theme="dark"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:disabled, +:root[data-theme="dark"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:disabled:hover { + background: var(--tz-turmeric-soft, #f0c463); + border-color: var(--tz-turmeric-soft, #f0c463); + color: #111827; + opacity: 1; + visibility: visible; + box-shadow: 0 8px 20px rgba(240, 196, 99, 0.18); +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:not(:disabled):hover { + background: #854d0e; + border-color: #854d0e; + color: #ffffff; +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:not(:disabled):hover { + background: #f6d889; + border-color: #f6d889; + color: #111827; +} + +:root[data-theme] + .fabric-search.fabric-search + .results-section + .pagination + .pagination__btn:disabled { + cursor: not-allowed; + filter: saturate(0.72); +} + +/* Center Clear and Recrop as one balanced vertical group. The rail remains + independent from image dimensions, so neither action can be clipped. */ +:root[data-theme] + .fabric-search.fabric-search + .image-preview__media--with-actions + .image-preview__action-rail { + justify-content: center; + gap: 28px; + padding: 12px 0; +} + +/* Clear Audio is an actual action, not a bare text link. Match the Upload + primary/secondary action language while keeping destructive confirmation in + the modal itself. */ +:root[data-theme] + .upload-page.upload-page + .audio-clear + .img-footer + .chip.chip-clear { + min-height: 44px; + padding: 10px 18px; + border-radius: 12px; + font-weight: 800; + line-height: 1; + white-space: nowrap; + text-shadow: none; + box-shadow: 0 6px 18px rgba(138, 79, 8, 0.2); + transform: none; +} + +:root[data-theme="light"] + .upload-page.upload-page + .audio-clear + .img-footer + .chip.chip-clear { + background: #a16207; + border: 1px solid #a16207; + color: #ffffff; +} + +:root[data-theme="dark"] + .upload-page.upload-page + .audio-clear + .img-footer + .chip.chip-clear { + background: var(--tz-turmeric-soft, #f0c463); + border: 1px solid var(--tz-turmeric-soft, #f0c463); + color: #111827; +} + +:root[data-theme="light"] + .upload-page.upload-page + .audio-clear + .img-footer + .chip.chip-clear:hover:not(:disabled) { + background: #854d0e; + border-color: #854d0e; + color: #ffffff; + transform: translateY(-1px); +} + +:root[data-theme="dark"] + .upload-page.upload-page + .audio-clear + .img-footer + .chip.chip-clear:hover:not(:disabled) { + background: #f6d889; + border-color: #f6d889; + color: #111827; + transform: translateY(-1px); +} + +@media (max-width: 680px) { + :root[data-theme] + .fabric-search.fabric-search + .image-preview__media--with-actions + .image-preview__action-rail { + justify-content: center; + gap: 12px; + padding: 0; + } +} + +/* Final Search preview alignment: lift only the Clear action while preserving + the balanced Recrop position and all existing image/action behavior. */ +@media (min-width: 681px) { + :root[data-theme] + .fabric-search.fabric-search + .image-preview__media--with-actions + .image-preview__action-rail + > .btn:first-child { + position: relative; + top: -28px; + } +} + +/* ============================================================ + Sticky image-search action visibility + ============================================================ + Recrop and New Image live in the fixed Search results toolbar. Give these + controls dedicated classes and a complete theme state so generic ghost / + outline rules cannot make them transparent or unreadable in dark mode. */ +:root[data-theme] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions { + opacity: 1; + visibility: visible; +} + +:root[data-theme] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 42px; + padding: 9px 16px; + border: 1px solid transparent; + border-radius: 999px; + opacity: 1; + visibility: visible; + filter: none; + backdrop-filter: none; + -webkit-backdrop-filter: none; + text-shadow: none; + transform: none; +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn { + background: #a16207; + border-color: #a16207; + color: #ffffff; + box-shadow: 0 8px 20px rgba(138, 79, 8, 0.18); +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn { + background: var(--tz-turmeric-soft, #f0c463); + border-color: var(--tz-turmeric-soft, #f0c463); + color: #111827; + box-shadow: 0 8px 20px rgba(240, 196, 99, 0.2); +} + +:root[data-theme="light"] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn:hover:not(:disabled), +:root[data-theme="light"] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn:focus-visible { + background: #854d0e; + border-color: #854d0e; + color: #ffffff; + box-shadow: 0 10px 24px rgba(133, 77, 14, 0.24); + transform: translateY(-1px); +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn:hover:not(:disabled), +:root[data-theme="dark"] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn:focus-visible { + background: #f6d889; + border-color: #f6d889; + color: #111827; + box-shadow: 0 10px 24px rgba(240, 196, 99, 0.24); + transform: translateY(-1px); +} + +:root[data-theme] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn:focus-visible { + outline: 3px solid rgba(224, 168, 46, 0.34); + outline-offset: 2px; +} + +/* Final sticky image-action foreground guard. + These controls intentionally own their presentation instead of inheriting + generic ghost/outline button colors from route styles loaded later. */ +:root[data-theme="light"] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn { + color: #ffffff !important; + -webkit-text-fill-color: #ffffff !important; +} + +:root[data-theme="dark"] + .fabric-search.fabric-search + .search-bar.search-bar--sticky + .search-bar__image-actions + .search-bar__image-action.btn { + color: #111827 !important; + -webkit-text-fill-color: #111827 !important; +} + +/* ============================================================ + Search crop dialog viewport containment + ============================================================ + Render the crop dialog through a document-level portal so transformed or + animated Search containers cannot redefine the containing block for its + fixed overlay. Keep the complete title, crop stage and actions visible in + the viewport; only the crop stage may scroll on unusually short screens. */ +.fabric-search-modal-root { + display: contents; +} + +:root[data-theme] + .fabric-search-modal-root.fabric-search + > .crop-drawer.crop-drawer { + position: fixed !important; + inset: 0 !important; + z-index: 2147483000; + box-sizing: border-box; + width: 100vw; + height: 100vh; + height: 100dvh; + min-height: 0; + padding: clamp(10px, 2vh, 20px) !important; + display: flex; + align-items: center !important; + justify-content: center !important; + overflow: hidden !important; + overscroll-behavior: contain; +} + +:root[data-theme] + .fabric-search-modal-root.fabric-search + > .crop-drawer.crop-drawer + > .crop-drawer__inner { + box-sizing: border-box; + width: min(720px, calc(100vw - 24px)) !important; + max-width: calc(100vw - 24px); + max-height: calc(100dvh - 20px) !important; + margin: 0 !important; + overflow: hidden !important; + display: flex; + flex-direction: column; +} + +:root[data-theme] .fabric-search-modal-root.fabric-search .crop-drawer__stage { + flex: 1 1 auto; + min-height: 0; + overflow: auto; + overscroll-behavior: contain; +} + +:root[data-theme] + .fabric-search-modal-root.fabric-search + :is(.crop-drawer__image-wrap, .crop-drawer__image) { + max-height: min(56dvh, calc(100dvh - 230px)) !important; +} + +:root[data-theme] + .fabric-search-modal-root.fabric-search + .crop-drawer__actions { + flex: 0 0 auto; +} + +@media (max-height: 680px) { + :root[data-theme] + .fabric-search-modal-root.fabric-search + > .crop-drawer.crop-drawer { + align-items: center !important; + padding: 8px !important; + } + + :root[data-theme] + .fabric-search-modal-root.fabric-search + > .crop-drawer.crop-drawer + > .crop-drawer__inner { + max-height: calc(100dvh - 16px) !important; + gap: 10px; + padding: 14px !important; + } + + :root[data-theme] + .fabric-search-modal-root.fabric-search + :is(.crop-drawer__image-wrap, .crop-drawer__image) { + max-height: calc(100dvh - 190px) !important; + } +} + + +/* ============================================================ + Unified user-facing connection errors + ============================================================ + Keep Search, Chat and List failures consistent and readable. The paired + foreground/background colors intentionally follow the requested theme rule: + white text in light mode and dark text in dark mode. */ +:root[data-theme] + :is( + .fabric-search.fabric-search .fabric-search__error, + .page-root .error[role="alert"], + .grid-page .grid-error[role="alert"] + ) { + box-sizing: border-box; + width: min(100%, 1180px); + min-height: 52px; + margin: 18px auto 0; + padding: 13px 20px; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + font-size: 0.98rem; + font-weight: 700; + line-height: 1.45; + border-radius: 10px; + text-wrap: balance; +} + +:root[data-theme="light"] + :is( + .fabric-search.fabric-search .fabric-search__error, + .page-root .error[role="alert"], + .grid-page .grid-error[role="alert"] + ) { + color: #ffffff !important; + -webkit-text-fill-color: #ffffff !important; + background: #b91c1c !important; + border: 1px solid #991b1b !important; + box-shadow: 0 10px 24px rgba(185, 28, 28, 0.22) !important; +} + +:root[data-theme="dark"] + :is( + .fabric-search.fabric-search .fabric-search__error, + .page-root .error[role="alert"], + .grid-page .grid-error[role="alert"] + ) { + color: #111827 !important; + -webkit-text-fill-color: #111827 !important; + background: #b91c1c !important; + border: 1px solid #991b1b !important; + box-shadow: 0 10px 24px rgba(185, 28, 28, 0.22) !important; +} + +:root[data-theme] + :is( + .fabric-search.fabric-search .fabric-search__error, + .page-root .error[role="alert"], + .grid-page .grid-error[role="alert"] + ) * { + color: inherit !important; + -webkit-text-fill-color: currentColor !important; +} + +@media (max-width: 680px) { + :root[data-theme] + :is( + .fabric-search.fabric-search .fabric-search__error, + .page-root .error[role="alert"], + .grid-page .grid-error[role="alert"] + ) { + width: calc(100% - 24px); + padding-inline: 14px; + font-size: 0.92rem; + } +} diff --git a/frontend/src/assets/styles/SuggestionChips.css b/frontend/src/assets/styles/SuggestionChips.css index 2b3e67d..54a9b5f 100644 --- a/frontend/src/assets/styles/SuggestionChips.css +++ b/frontend/src/assets/styles/SuggestionChips.css @@ -82,3 +82,57 @@ color: #f8fafc ; background: rgba(30, 41, 59, 0.96) ; } + +/* Senior QA top-up: desktop prompt chips must stay aligned and readable at 100-125% zoom. */ +.page-root.is-empty .empty-state-large .sample-prompts, +.page-root.is-empty .prompt-buttons, +.page-root.is-empty .quick-question-row, +.page-root.is-empty .empty-state-actions { + width: min(1120px, calc(100vw - 56px)); + margin-inline: auto; +} + +.page-root.is-empty .empty-state-large .chip, +.page-root.is-empty .sample-prompts .chip, +.page-root.is-empty .prompt-buttons button, +.page-root.is-empty .quick-question-row button { + min-height: 54px; + display: inline-flex; + align-items: center; + justify-content: center; + white-space: nowrap; + line-height: 1.15; +} + +@media (min-width: 980px) { + .page-root.is-empty .empty-state-large .sample-prompts, + .page-root.is-empty .prompt-buttons, + .page-root.is-empty .quick-question-row, + .page-root.is-empty .empty-state-actions { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 14px; + } + + .page-root.is-empty .empty-state-large .chip, + .page-root.is-empty .sample-prompts .chip, + .page-root.is-empty .prompt-buttons button, + .page-root.is-empty .quick-question-row button { + width: 100%; + max-width: none; + padding-inline: 14px; + font-size: clamp(13px, 0.95vw, 16px); + } +} + +@media (max-width: 979px) { + .page-root.is-empty .empty-state-large .sample-prompts, + .page-root.is-empty .prompt-buttons, + .page-root.is-empty .quick-question-row, + .page-root.is-empty .empty-state-actions { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 12px; + } +} diff --git a/frontend/src/assets/styles/UploadPage.css b/frontend/src/assets/styles/UploadPage.css index fe9649d..1aa8519 100644 --- a/frontend/src/assets/styles/UploadPage.css +++ b/frontend/src/assets/styles/UploadPage.css @@ -493,3 +493,141 @@ background: #dc2626; border-color: rgba(127, 29, 29, 0.22); } + +/* v39: post-selection Upload layout on narrow phones. Keep every preview, + audio control, file-name field and action inside the card without changing + upload/record/submit behavior. Tablet and desktop layouts are untouched. */ +@media (max-width: 480px) { + .upload-page { + width: 100%; + min-height: 0; + padding: 18px 10px 30px; + } + + .upload-page .upload-card { + box-sizing: border-box; + width: 100%; + min-width: 0; + min-height: 0; + padding: 16px 12px; + border-radius: 16px; + overflow: visible; + } + + .upload-page .upload-header { + margin-bottom: 16px; + } + + .upload-page .upload-header h2 { + font-size: clamp(20px, 6.4vw, 23px); + } + + .upload-page .sub { + font-size: 13px; + line-height: 1.4; + } + + .upload-page .grid, + .upload-page .preview-col, + .upload-page .action-col, + .upload-page .audio-card, + .upload-page .audio-clear, + .upload-page .audio-player, + .upload-page .recording-area, + .upload-page .name-field, + .upload-page .name-field > div, + .upload-page .submit-wrapper { + box-sizing: border-box; + width: 100%; + min-width: 0; + max-width: 100%; + } + + .upload-page .grid { + gap: 16px; + } + + .upload-page .dropzone, + .upload-page .image-preview-wrap { + height: clamp(180px, 48vw, 210px); + } + + .upload-page .image-preview-plain { + object-fit: contain; + } + + .upload-page .rec-controls { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + width: 100%; + gap: 8px; + } + + .upload-page .rec-controls .btn { + width: 100%; + min-width: 0; + padding-inline: 8px; + white-space: normal; + } + + .upload-page .audio-clear { + flex-direction: column; + align-items: stretch; + gap: 10px; + margin-top: 10px; + } + + .upload-page .audio-player { + margin-top: 0; + } + + .upload-page .audio-player audio { + display: block; + width: 100%; + max-width: 100%; + } + + .upload-page .img-footer { + display: flex; + justify-content: center; + width: 100%; + margin-top: 0; + } + + .upload-page .audio-clear .chip.chip-clear { + width: min(100%, 190px); + white-space: normal; + } + + .upload-page .name-field { + display: grid; + grid-template-columns: minmax(0, 1fr); + align-items: stretch; + margin-top: 18px; + gap: 10px; + } + + .upload-page .name-label { + margin: 0; + font-size: 15px; + line-height: 1.3; + text-align: left; + } + + .upload-page input[type="text"] { + box-sizing: border-box; + width: 100%; + max-width: 100%; + min-width: 0; + font-size: 16px; + } + + .upload-page .btn.submit, + .upload-page .cancel { + box-sizing: border-box; + width: 100%; + min-width: 0; + min-height: 42px; + padding-inline: 14px; + } +} diff --git a/frontend/src/assets/styles/_makeover.css b/frontend/src/assets/styles/_makeover.css index 011c82f..f879e62 100644 --- a/frontend/src/assets/styles/_makeover.css +++ b/frontend/src/assets/styles/_makeover.css @@ -43,33 +43,63 @@ --tz-dropzone-hover-bg: rgba(27, 34, 48, 0.78); --tz-upload-panel-bg: rgba(23, 31, 51, 0.94); --tz-lightbox-bg: rgba(8, 11, 20, 0.86); - --reader-page-bg: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0f0f0f 100%); - --reader-title-gradient: linear-gradient(135deg, #fff 0%, #aaa 100%); - --reader-muted: #888; - --reader-text: #fff; - --reader-text-soft: #aaa; - --reader-tile-bg: linear-gradient(135deg, rgba(255, 107, 53, 0.06) 0%, rgba(247, 147, 30, 0.03) 100%); - --reader-tile-hover-bg: linear-gradient(135deg, rgba(255, 107, 53, 0.12) 0%, rgba(247, 147, 30, 0.08) 100%); - --reader-tile-border: rgba(255, 107, 53, 0.15); - --reader-tile-border-hover: rgba(255, 107, 53, 0.5); - --reader-tile-glow: radial-gradient(circle at 50% 50%, rgba(255, 107, 53, 0.12), transparent 70%); - --reader-tile-hover-shadow: 0 32px 80px rgba(255, 107, 53, 0.3); - --document-reader-page-bg: var(--reader-page-bg); - --document-reader-title-gradient: var(--reader-title-gradient); - --document-reader-text: #fff; - --document-reader-muted: #888; - --document-reader-muted-strong: #999; - --document-reader-surface: #1a1a1a; - --document-reader-surface-soft: rgba(255, 255, 255, 0.05); - --document-reader-surface-subtle: rgba(255, 255, 255, 0.03); - --document-reader-surface-faint: rgba(255, 255, 255, 0.02); - --document-reader-border: rgba(255, 255, 255, 0.1); - --document-reader-border-soft: rgba(255, 255, 255, 0.05); - --document-reader-card-bg: linear-gradient(135deg, #1a1a1a 0%, #252525 100%); - --document-reader-crop-bg: #000; - --document-reader-modal-bg: #1a1a1a; - --document-reader-modal-overlay: rgba(0, 0, 0, 0.95); - --document-reader-preview-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); + --tz-ui-page: var(--tz-app-bg); + --tz-ui-surface: rgba(15, 23, 42, 0.78); + --tz-ui-surface-raised: rgba(23, 31, 51, 0.96); + --tz-ui-surface-subtle: rgba(30, 41, 59, 0.72); + --tz-ui-control-bg: rgba(30, 41, 59, 0.96); + --tz-ui-control-hover-bg: rgba(51, 65, 85, 0.98); + --tz-ui-text: #f8fafc; + --tz-ui-text-soft: #e2e8f0; + --tz-ui-text-muted: #cbd5e1; + --tz-ui-text-faint: #94a3b8; + --tz-ui-border: rgba(226, 232, 240, 0.22); + --tz-ui-border-strong: rgba(226, 232, 240, 0.38); + --tz-ui-accent: var(--tz-turmeric); + --tz-ui-accent-contrast: #171101; + --tz-ui-focus-ring: rgba(224, 168, 46, 0.34); + --tz-ui-shadow-panel: 0 18px 48px rgba(0, 0, 0, 0.28); + /* Reader pages consume the same theme and typography tokens as the rest of the app. */ + --reader-page-bg: var(--tz-page-bg); + --reader-muted: var(--tz-ui-text-muted); + --reader-text: var(--tz-ui-text); + --reader-text-soft: var(--tz-ui-text-muted); + --reader-tile-bg: var(--tz-ui-surface); + --reader-tile-hover-bg: var(--tz-ui-surface-raised); + --reader-tile-border: var(--tz-ui-border); + --reader-tile-border-hover: var(--tz-accent-control-surface, var(--tz-ui-accent)); + --reader-tile-glow: radial-gradient(circle at 50% 50%, rgba(224, 168, 46, 0.12), transparent 70%); + --reader-tile-hover-shadow: var(--tz-ui-shadow-panel); + --reader-icon-shadow: drop-shadow(0 8px 24px rgba(224, 168, 46, 0.24)); + --document-reader-page-bg: var(--tz-page-bg); + --document-reader-text: var(--tz-ui-text); + --document-reader-muted: var(--tz-ui-text-muted); + --document-reader-muted-strong: var(--tz-ui-text-faint); + --document-reader-surface: var(--tz-ui-surface-raised); + --document-reader-surface-soft: var(--tz-ui-surface-subtle); + --document-reader-surface-subtle: rgba(30, 41, 59, 0.72); + --document-reader-surface-faint: rgba(30, 41, 59, 0.46); + --document-reader-border: var(--tz-ui-border); + --document-reader-border-soft: rgba(226, 232, 240, 0.14); + --document-reader-card-bg: var(--tz-ui-surface-raised); + --document-reader-crop-bg: var(--tz-ink-3); + --document-reader-modal-bg: var(--tz-ui-surface-raised); + --document-reader-modal-overlay: var(--tz-lightbox-bg); + --document-reader-preview-shadow: var(--tz-ui-shadow-panel); + --document-reader-accent: var(--tz-accent-control-surface, var(--tz-ui-accent)); + --document-reader-accent-hover: var(--tz-accent-control-surface-hover, var(--tz-turmeric-soft)); + --document-reader-accent-contrast: var(--tz-accent-control-foreground, #111827); + --document-reader-accent-soft: rgba(224, 168, 46, 0.12); + --document-reader-accent-border: rgba(224, 168, 46, 0.34); + --document-reader-accent-border-soft: rgba(224, 168, 46, 0.20); + --document-reader-accent-muted: rgba(240, 196, 99, 0.72); + --document-reader-accent-shadow: 0 12px 36px rgba(224, 168, 46, 0.24); + --document-reader-primary-bg: linear-gradient(135deg, var(--document-reader-accent) 0%, var(--document-reader-accent-hover) 100%); + --document-reader-spinner-track: rgba(17, 24, 39, 0.24); + --document-reader-spinner-head: #111827; + --document-reader-danger-text: #fecaca; + --document-reader-danger-bg: rgba(185, 75, 54, 0.14); + --document-reader-danger-border: rgba(208, 92, 69, 0.42); } :root[data-theme="dark"] { @@ -111,33 +141,62 @@ --tz-dropzone-hover-bg: rgba(241, 245, 249, 0.96); --tz-upload-panel-bg: rgba(255, 255, 255, 0.90); --tz-lightbox-bg: rgba(17, 24, 39, 0.54); - --reader-page-bg: linear-gradient(135deg, #f8fafc 0%, #eef4fb 50%, #ffffff 100%); - --reader-title-gradient: linear-gradient(135deg, #111827 0%, #365ea8 100%); - --reader-muted: #4b5563; - --reader-text: #111827; - --reader-text-soft: #4b5563; - --reader-tile-bg: linear-gradient(135deg, rgba(255, 255, 255, 0.94) 0%, rgba(241, 245, 249, 0.86) 100%); - --reader-tile-hover-bg: linear-gradient(135deg, rgba(255, 255, 255, 1) 0%, rgba(231, 238, 247, 0.98) 100%); - --reader-tile-border: rgba(54, 94, 168, 0.18); - --reader-tile-border-hover: rgba(54, 94, 168, 0.42); - --reader-tile-glow: radial-gradient(circle at 50% 50%, rgba(54, 94, 168, 0.12), transparent 70%); - --reader-tile-hover-shadow: 0 28px 70px rgba(31, 41, 55, 0.12); - --document-reader-page-bg: var(--reader-page-bg); - --document-reader-title-gradient: var(--reader-title-gradient); - --document-reader-text: #111827; - --document-reader-muted: #4b5563; - --document-reader-muted-strong: #64748b; - --document-reader-surface: #ffffff; - --document-reader-surface-soft: rgba(37, 99, 235, 0.07); - --document-reader-surface-subtle: rgba(37, 99, 235, 0.05); - --document-reader-surface-faint: rgba(37, 99, 235, 0.035); - --document-reader-border: rgba(37, 99, 235, 0.14); - --document-reader-border-soft: rgba(37, 99, 235, 0.10); - --document-reader-card-bg: linear-gradient(135deg, #ffffff 0%, #f1f5f9 100%); - --document-reader-crop-bg: #eef4fb; - --document-reader-modal-bg: #ffffff; - --document-reader-modal-overlay: rgba(17, 24, 39, 0.58); - --document-reader-preview-shadow: 0 20px 54px rgba(31, 41, 55, 0.14); + --tz-ui-page: var(--tz-app-bg); + --tz-ui-surface: rgba(255, 255, 255, 0.82); + --tz-ui-surface-raised: rgba(255, 255, 255, 0.96); + --tz-ui-surface-subtle: rgba(241, 245, 249, 0.86); + --tz-ui-control-bg: rgba(255, 255, 255, 0.96); + --tz-ui-control-hover-bg: #edf2f8; + --tz-ui-text: #0f172a; + --tz-ui-text-soft: #1f2937; + --tz-ui-text-muted: #475569; + --tz-ui-text-faint: #64748b; + --tz-ui-border: rgba(15, 23, 42, 0.14); + --tz-ui-border-strong: rgba(15, 23, 42, 0.24); + --tz-ui-accent: var(--tz-turmeric); + --tz-ui-accent-contrast: #171101; + --tz-ui-focus-ring: rgba(183, 121, 31, 0.28); + --tz-ui-shadow-panel: 0 18px 44px rgba(31, 41, 55, 0.12); + --reader-page-bg: var(--tz-page-bg); + --reader-muted: var(--tz-ui-text-muted); + --reader-text: var(--tz-ui-text); + --reader-text-soft: var(--tz-ui-text-muted); + --reader-tile-bg: var(--tz-ui-surface); + --reader-tile-hover-bg: var(--tz-ui-surface-raised); + --reader-tile-border: var(--tz-ui-border); + --reader-tile-border-hover: var(--tz-accent-control-surface, var(--tz-ui-accent)); + --reader-tile-glow: radial-gradient(circle at 50% 50%, rgba(138, 79, 8, 0.10), transparent 70%); + --reader-tile-hover-shadow: var(--tz-ui-shadow-panel); + --reader-icon-shadow: drop-shadow(0 8px 24px rgba(138, 79, 8, 0.18)); + --document-reader-page-bg: var(--tz-page-bg); + --document-reader-text: var(--tz-ui-text); + --document-reader-muted: var(--tz-ui-text-muted); + --document-reader-muted-strong: var(--tz-ui-text-faint); + --document-reader-surface: var(--tz-ui-surface-raised); + --document-reader-surface-soft: var(--tz-ui-surface-subtle); + --document-reader-surface-subtle: rgba(241, 245, 249, 0.86); + --document-reader-surface-faint: rgba(241, 245, 249, 0.62); + --document-reader-border: var(--tz-ui-border); + --document-reader-border-soft: rgba(15, 23, 42, 0.10); + --document-reader-card-bg: var(--tz-ui-surface-raised); + --document-reader-crop-bg: var(--tz-panel-2); + --document-reader-modal-bg: var(--tz-ui-surface-raised); + --document-reader-modal-overlay: var(--tz-lightbox-bg); + --document-reader-preview-shadow: var(--tz-ui-shadow-panel); + --document-reader-accent: var(--tz-accent-control-surface, var(--tz-ui-accent)); + --document-reader-accent-hover: var(--tz-accent-control-surface-hover, var(--tz-turmeric-soft)); + --document-reader-accent-contrast: var(--tz-accent-control-foreground, #ffffff); + --document-reader-accent-soft: rgba(138, 79, 8, 0.10); + --document-reader-accent-border: rgba(138, 79, 8, 0.30); + --document-reader-accent-border-soft: rgba(138, 79, 8, 0.18); + --document-reader-accent-muted: rgba(138, 79, 8, 0.72); + --document-reader-accent-shadow: 0 12px 36px rgba(138, 79, 8, 0.18); + --document-reader-primary-bg: linear-gradient(135deg, var(--document-reader-accent) 0%, var(--document-reader-accent-hover) 100%); + --document-reader-spinner-track: rgba(255, 255, 255, 0.35); + --document-reader-spinner-head: #ffffff; + --document-reader-danger-text: #991b1b; + --document-reader-danger-bg: rgba(254, 226, 226, 0.95); + --document-reader-danger-border: rgba(220, 38, 38, 0.35); } /* ============================================================ @@ -2287,3 +2346,53 @@ footer a:hover, .lb-caption { color: var(--tz-ui-text-muted) ; } + +/* ============================================================ + Senior QA visual-system hardening: shared tokens and states + ============================================================ */ +:root { + --tz-action-yellow: #e0a82e; + --tz-action-yellow-hover: #f0c463; + --tz-action-yellow-text: #171101; + --tz-danger: #dc2626; + --tz-danger-hover: #b91c1c; + --tz-danger-text: #ffffff; + --tz-focus: 0 0 0 3px var(--tz-ui-focus-ring, rgba(224, 168, 46, 0.34)); + --tz-control-height: 44px; + --tz-control-radius: 999px; +} + +button:focus-visible, +a:focus-visible, +[role="button"]:focus-visible, +input:focus-visible, +textarea:focus-visible, +select:focus-visible { + outline: 2px solid var(--tz-ui-accent, var(--tz-action-yellow)); + outline-offset: 3px; + box-shadow: var(--tz-focus); +} + +button, +[role="button"] { + -webkit-tap-highlight-color: transparent; +} + +.btn, +button { + text-shadow: none; +} + +.btn:disabled, +button:disabled { + opacity: 0.68; + cursor: not-allowed; +} + +.site-header { + min-height: 64px; +} + +.main-content { + min-height: calc(100dvh - 64px); +} diff --git a/frontend/src/assets/styles/navbar.css b/frontend/src/assets/styles/navbar.css index 5c759ad..689ea32 100644 --- a/frontend/src/assets/styles/navbar.css +++ b/frontend/src/assets/styles/navbar.css @@ -1,130 +1,129 @@ -.header-nav ul { - list-style: none; - padding: 0; - margin: 0; -} - -.header-nav a { - text-decoration: none; - color: #222; - font-weight: 500; - display: block; - padding: 6px 16px; - border-radius: 999px; - transition: background 0.18s ease, color 0.18s ease; -} - -.header-nav a.active { - /* pill background is applied by _makeover.css; border-bottom is superseded by pill style */ -} - -.nav-toggle { - display: none; -} - -@media (min-width: 769px) { - - /* desktop layout stays intact */ - .hamburger { - display: none ; - } - - .header-nav ul { - position: static; - display: flex; - flex-direction: row; - align-items: center; - gap: 0.25rem; - background: none; - padding: 0; - transform: none; - opacity: 1; - pointer-events: auto; - height: auto; - box-shadow: none; - top: 1.5rem; - } -} - -@media (max-width: 768px) { - - .mobile-nav-wrapper { - position: relative; - z-index: 999; - } - - /* position hamburger & cross */ - .hamburger { - display: flex; - width: 30px; - height: 22px; - flex-direction: column; - justify-content: space-between; - cursor: pointer; - - position: fixed; - /* always visible */ - top: 16px; - right: 16px; - /* 🔹 fully right aligned */ - z-index: 1002; - /* above overlay */ - } - - .hamburger span { - height: 3px; - width: 100%; - background: #222; - border-radius: 3px; - transition: 0.3s ease; - } - - /* FULLSCREEN OVERLAY */ - .header-nav ul { - position: fixed; - inset: 0; - background: #ffffffee; - backdrop-filter: blur(4px); - display: flex; - flex-direction: column; - padding-top: 100px; - padding-left: 24px; - padding-right: 24px; - gap: 1.2rem; - transform: translateY(-100%); - opacity: 0; - pointer-events: none; - transition: 0.35s ease; - } - - /* SHOW MENU */ - .nav-toggle:checked~.header-nav ul { - transform: translateY(0); - opacity: 1; - pointer-events: auto; - } - - /* ANIMATE BUTTON INTO X */ - .nav-toggle:checked+.hamburger span:nth-child(1) { - transform: translateY(9px) rotate(45deg); - } - - .nav-toggle:checked+.hamburger span:nth-child(2) { - opacity: 0; - } - - .nav-toggle:checked+.hamburger span:nth-child(3) { - transform: translateY(-9px) rotate(-45deg); - } - - /* MOBILE ITEM STYLE */ - .header-nav a { - padding: 14px 10px; - font-size: 18px; - border-radius: 6px; - } - - .header-nav a.active { - background: rgba(0, 0, 0, 0.1); - } -} \ No newline at end of file +.header-nav ul { + list-style: none; + padding: 0; + margin: 0; +} + +.header-nav a { + text-decoration: none; + color: #222; + font-weight: 500; + display: block; + padding: 6px 16px; + border-radius: 999px; + transition: + background 0.18s ease, + color 0.18s ease; +} + +.header-nav a.active { + /* pill background is applied by _makeover.css; border-bottom is superseded by pill style */ +} + +@media (min-width: 769px) { + /* desktop layout stays intact */ + .hamburger { + display: none; + } + + .header-nav ul { + position: static; + display: flex; + flex-direction: row; + align-items: center; + gap: 0.25rem; + background: none; + padding: 0; + transform: none; + opacity: 1; + pointer-events: auto; + height: auto; + box-shadow: none; + top: 1.5rem; + } +} + +@media (max-width: 768px) { + .mobile-nav-wrapper { + position: relative; + z-index: 999; + } + + /* position hamburger & cross */ + .hamburger { + display: flex; + width: 30px; + height: 22px; + flex-direction: column; + justify-content: space-between; + cursor: pointer; + padding: 0; + border: 0; + background: transparent; + + position: fixed; + /* always visible */ + top: 16px; + right: 16px; + /* 🔹 fully right aligned */ + z-index: 1002; + /* above overlay */ + } + + .hamburger span { + height: 3px; + width: 100%; + background: #222; + border-radius: 3px; + transition: 0.3s ease; + } + + /* FULLSCREEN OVERLAY */ + .header-nav ul { + position: fixed; + inset: 0; + background: #ffffffee; + backdrop-filter: blur(4px); + display: flex; + flex-direction: column; + padding-top: 100px; + padding-left: 24px; + padding-right: 24px; + gap: 1.2rem; + transform: translateY(-100%); + opacity: 0; + pointer-events: none; + transition: 0.35s ease; + } + + /* SHOW MENU */ + .header-nav[data-open="true"] ul { + transform: translateY(0); + opacity: 1; + pointer-events: auto; + } + + /* ANIMATE BUTTON INTO X */ + .hamburger[aria-expanded="true"] span:nth-child(1) { + transform: translateY(9px) rotate(45deg); + } + + .hamburger[aria-expanded="true"] span:nth-child(2) { + opacity: 0; + } + + .hamburger[aria-expanded="true"] span:nth-child(3) { + transform: translateY(-9px) rotate(-45deg); + } + + /* MOBILE ITEM STYLE */ + .header-nav a { + padding: 14px 10px; + font-size: 18px; + border-radius: 6px; + } + + .header-nav a.active { + background: rgba(0, 0, 0, 0.1); + } +} diff --git a/frontend/src/components/AppErrorBoundary.tsx b/frontend/src/components/AppErrorBoundary.tsx new file mode 100644 index 0000000..9cc16d2 --- /dev/null +++ b/frontend/src/components/AppErrorBoundary.tsx @@ -0,0 +1,78 @@ +import type { ErrorInfo, ReactNode } from "react"; +import { Component } from "react"; + +type Props = { + children: ReactNode; + resetKey: string; +}; + +type State = { + hasError: boolean; + errorMessage: string; +}; + +/** + * Keeps a route render/lazy-load failure from blanking the entire application. + * Changing routes resets the boundary so navigation remains usable. + */ +const ROUTE_ASSET_ERROR_PATTERN = + /dynamically imported module|loading chunk|module script|failed to fetch/i; + +export class AppErrorBoundary extends Component { + state: State = { hasError: false, errorMessage: "" }; + + static getDerivedStateFromError(error: unknown): State { + return { + hasError: true, + errorMessage: error instanceof Error ? error.message : String(error ?? ""), + }; + } + + componentDidCatch(error: Error, info: ErrorInfo) { + if (import.meta.env.DEV) { + console.error("Route render failed", error, info.componentStack); + } + } + + componentDidUpdate(previousProps: Props) { + if (this.state.hasError && previousProps.resetKey !== this.props.resetKey) { + this.setState({ hasError: false, errorMessage: "" }); + } + } + + private retry = () => { + if (typeof window !== "undefined") { + window.location.reload(); + return; + } + + this.setState({ hasError: false, errorMessage: "" }); + }; + + render() { + if (!this.state.hasError) return this.props.children; + + const routeAssetFailed = ROUTE_ASSET_ERROR_PATTERN.test( + this.state.errorMessage, + ); + + return ( +
    +
    +

    Something went wrong

    +

    + {routeAssetFailed + ? "The page files could not be loaded. Disable browser request blocking or restore the network, then reload the page." + : "This page could not be displayed. Your uploaded files and chat input have not been submitted again."} +

    +
    + + Return home +
    +
    +
    + ); + } +} diff --git a/frontend/src/components/Composer.tsx b/frontend/src/components/Composer.tsx index 7c61014..f47b9ae 100644 --- a/frontend/src/components/Composer.tsx +++ b/frontend/src/components/Composer.tsx @@ -307,7 +307,11 @@ export default function Composer({ }; // NOTE: keep the rest of your logic unchanged (recording functions etc.) - const handleChipActionDefault = (actionId: string, _opts?: { name?: string }) => { + const handleChipActionDefault = ( + actionId: string, + opts?: { name?: string }, + ) => { + void opts; if (actionId === "image:analyze_short") { setMode("analysis"); onChange(textForAnalysisShort); @@ -350,7 +354,7 @@ export default function Composer({ const startRecording = async () => { setError(null); setInfo(null); - if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { + if (!navigator.mediaDevices?.getUserMedia) { setError("Microphone not supported in this browser."); return; } @@ -456,7 +460,6 @@ export default function Composer({ if (pendingImage) try { URL.revokeObjectURL(pendingImage.url); } catch (e) { console.log(e) } if (pendingAudio) try { URL.revokeObjectURL(pendingAudio.url); } catch (e) { console.log(e) } }; - // eslint-disable-next-line react-hooks/exhaustive-deps }, [pendingImage, pendingAudio]); useEffect(() => { @@ -533,19 +536,19 @@ export default function Composer({ padding: 16, }} > -
    -
    -
    - Confirm preview +
    +
    +
    + Confirm preview
    -
    -
    Upload this image?
    -
    {pendingImage.file.name} — {formatSize(pendingImage.file.size)}
    -
    - -
    @@ -571,22 +574,22 @@ export default function Composer({ padding: 16, }} > -
    -
    -
    -
    -
    -
    Upload this audio?
    -
    {pendingAudio.file.name} — {formatSize(pendingAudio.file.size)}
    -
    +
    +
    +
    +
    Upload this audio?
    +
    {pendingAudio.file.name} — {formatSize(pendingAudio.file.size)}
    +
    +
    +
    If longer than 1 minute we will trim it automatically.
    -
    - -
    @@ -876,20 +879,41 @@ export default function Composer({ }} /> {pendingRemove && ( -
    -
    -
    -
    -
    - {pendingRemove.kind === "image" ? "Image" : "Audio"} -
    +
    +
    +
    + -
    -
    {pendingRemove.kind === "image" ? "Remove this image?" : "Remove this audio?"}
    - {pendingRemove.name &&
    {pendingRemove.name}
    } -
    - - +
    +
    + {pendingRemove.kind === "image" ? "Remove this image?" : "Remove this audio?"} +
    + {pendingRemove.name && ( +
    + {pendingRemove.name} +
    + )} +
    + +
    diff --git a/frontend/src/components/DescriptionBox.tsx b/frontend/src/components/DescriptionBox.tsx index 2e44f2a..2bfaa41 100644 --- a/frontend/src/components/DescriptionBox.tsx +++ b/frontend/src/components/DescriptionBox.tsx @@ -4,6 +4,18 @@ import { FaRegCopy } from "react-icons/fa"; import "@/assets/styles/DescriptionBox.css"; import Loader from "./Loader"; +type DescriptionBoxProps = { + isValidImage: boolean | null; + validationMessage: string; + loading: boolean; + responses: string[]; + currentIndex?: number; + typedText?: string; + description?: string; + handlePrev: () => void; + handleNext: () => void | Promise; +}; + const DescriptionBox = ({ isValidImage, validationMessage, @@ -14,7 +26,7 @@ const DescriptionBox = ({ description = "", handlePrev, handleNext, -}) => { +}: DescriptionBoxProps) => { const [copied, setCopied] = useState(false); const isError = isValidImage === false && validationMessage; @@ -68,8 +80,10 @@ const DescriptionBox = ({
    - {/* Copy action: keep the UI clean and avoid showing response-count text. */} {!loading && !isError && hasResponses && (
    + + {currentIndex + 1}/{responses.length} + + + + + )} +
    + )}
    {hasImage ? (
    Preview @@ -114,50 +177,6 @@ const ImagePreviewPanel = ({ )}
    - {showButtons && ( -
    - {validationLoading ? ( -

    - 🔍 Checking if this is a valid fabric image... -

    - ) : ( - <> - - - - - )} -
    - )} - {showConfirm && (
    diff --git a/frontend/src/components/MessageList.tsx b/frontend/src/components/MessageList.tsx index 2ea5b43..377c2ad 100644 --- a/frontend/src/components/MessageList.tsx +++ b/frontend/src/components/MessageList.tsx @@ -30,6 +30,22 @@ export default function MessageList({ const lastAssistantRef = useRef(null); const lastAssistantMsgRef = useRef(null); const rafRef = useRef(null); + const messageKeysRef = useRef(new WeakMap()); + const nextMessageKeyRef = useRef(0); + + const getMessageKey = (message: Message): string => { + const messageWithId = message as Message & { id?: unknown }; + if (typeof messageWithId.id === "string" && messageWithId.id.trim()) { + return `id:${messageWithId.id}`; + } + + const existing = messageKeysRef.current.get(message); + if (existing) return existing; + + const generated = `message:${nextMessageKeyRef.current++}`; + messageKeysRef.current.set(message, generated); + return generated; + }; // Always keep scroll pinned to bottom on commit useLayoutEffect(() => { @@ -148,7 +164,6 @@ export default function MessageList({ rafRef.current = null; } }; - // eslint-disable-next-line react-hooks/exhaustive-deps }, [messages, onLastAssistantRendered, scrollerRef]); // helper to decide whether a message contains the "ask more" prompt @@ -214,7 +229,7 @@ export default function MessageList({ return (
    void; +}; + +const SunIcon = () => ( + +); + +const MoonIcon = () => ( + +); + +export const NavBar: React.FC = ({ theme, onToggleTheme }) => { + const [isOpen, setIsOpen] = useState(false); + const location = useLocation(); + const toggleRef = useRef(null); + const navRef = useRef(null); + const navClass = ({ isActive }: NavLinkRenderProps) => + isActive ? "active" : ""; + + useEffect(() => { + if (location.pathname) setIsOpen(false); + }, [location.pathname]); + + useEffect(() => { + if (!isOpen) return; + + const previousOverflow = document.body.style.overflow; + document.body.style.overflow = "hidden"; + + const firstLink = navRef.current?.querySelector( + 'a[href]:not([aria-hidden="true"])', + ); + firstLink?.focus(); + + const onKeyDown = (event: KeyboardEvent) => { + if (event.key !== "Escape") return; + setIsOpen(false); + toggleRef.current?.focus(); + }; + + document.addEventListener("keydown", onKeyDown); + return () => { + document.body.style.overflow = previousOverflow; + document.removeEventListener("keydown", onKeyDown); + }; + }, [isOpen]); -export const NavBar: React.FC = () => { - const navClass = ({ isActive }) => (isActive ? "active" : ""); return (
    - - - - -
    diff --git a/frontend/src/components/SampleImageGalleryCard.tsx b/frontend/src/components/SampleImageGalleryCard.tsx index 85f4540..8bf958c 100644 --- a/frontend/src/components/SampleImageGalleryCard.tsx +++ b/frontend/src/components/SampleImageGalleryCard.tsx @@ -1,12 +1,20 @@ +import sample1Image from "../assets/sample1.jpeg"; +import sample2Image from "../assets/sample2.jpeg"; +import sample3Image from "../assets/sample3.jpeg"; import "@/assets/styles/SampleImageGalleryCard.css"; const sampleImages = [ - { id: 1, name: "Sample 1", path: "../assets/sample1.jpeg" }, - { id: 2, name: "Sample 2", path: "../assets/sample2.jpeg" }, - { id: 3, name: "Sample 3", path: "../assets/sample3.jpeg" }, + { id: 1, name: "Sample 1", path: sample1Image }, + { id: 2, name: "Sample 2", path: sample2Image }, + { id: 3, name: "Sample 3", path: sample3Image }, ]; -const SampleImageGallery = ({ onAnalyze, loading }) => { +type SampleImageGalleryProps = { + onAnalyze: (samplePath: string) => void | Promise; + loading: boolean; +}; + +const SampleImageGallery = ({ onAnalyze, loading }: SampleImageGalleryProps) => { return (

    Try with Sample Images

    @@ -21,7 +29,13 @@ const SampleImageGallery = ({ onAnalyze, loading }) => { aria-label={`Analyze ${img.name}`} onClick={() => onAnalyze(img.path)} > - {img.name} + {img.name} diff --git a/frontend/src/components/search/CategoryPicker.tsx b/frontend/src/components/search/CategoryPicker.tsx new file mode 100644 index 0000000..1f7fba4 --- /dev/null +++ b/frontend/src/components/search/CategoryPicker.tsx @@ -0,0 +1,76 @@ +import { useEffect, useState } from "react"; +import { CATEGORIES } from "./searchConfig"; + +interface CategoryPickerProps { + selected: string[]; + onChange: (cats: string[]) => void; + compact?: boolean; +} + +export default function CategoryPicker({ selected, onChange, compact = false }: CategoryPickerProps) { + const [tempSelected, setTempSelected] = useState(selected); + + const toggle = (id: string) => { + const next = tempSelected.includes(id) + ? tempSelected.filter((c) => c !== id) + : [...tempSelected, id]; + setTempSelected(next); + if (!compact) { + onChange(next); + } + }; + + const allOn = tempSelected.length === CATEGORIES.length; + const hasPendingChanges = compact && ( + tempSelected.length !== selected.length || + tempSelected.some((id) => !selected.includes(id)) || + selected.some((id) => !tempSelected.includes(id)) + ); + const toggleAll = () => { + const next = allOn ? [] : CATEGORIES.map((c) => c.id); + setTempSelected(next); + if (!compact) { + onChange(next); + } + }; + + useEffect(() => { + setTempSelected(selected); + }, [selected]); + + return ( +
    +
    + Filter by Category + +
    + +
    + {CATEGORIES.map((cat) => { + const active = tempSelected.includes(cat.id); + return ( + + ); + })} + {compact && hasPendingChanges && ( + + )} +
    +
    + ); +} diff --git a/frontend/src/components/search/CropDrawer.tsx b/frontend/src/components/search/CropDrawer.tsx new file mode 100644 index 0000000..634ffa0 --- /dev/null +++ b/frontend/src/components/search/CropDrawer.tsx @@ -0,0 +1,104 @@ +import { useEffect } from "react"; +import type { RefObject, MouseEvent as ReactMouseEvent } from "react"; +import { createPortal } from "react-dom"; +import type { CropRect } from "./types"; + +interface CropDrawerProps { + rawImageUrl: string; + cropRect: CropRect; + imgRef: RefObject; + onImageLoad: () => void; + onDragStart: (e: ReactMouseEvent) => void; + onResizeStart: (e: ReactMouseEvent) => void; + onConfirm: () => void; + onCancel: () => void; +} + +export default function CropDrawer({ + rawImageUrl, + cropRect, + imgRef, + onImageLoad, + onDragStart, + onResizeStart, + onConfirm, + onCancel, +}: CropDrawerProps) { + useEffect(() => { + const previousBodyOverflow = document.body.style.overflow; + const previousDocumentOverflow = document.documentElement.style.overflow; + + document.body.style.overflow = "hidden"; + document.documentElement.style.overflow = "hidden"; + + return () => { + document.body.style.overflow = previousBodyOverflow; + document.documentElement.style.overflow = previousDocumentOverflow; + }; + }, []); + + const dialog = ( +
    +
    +
    +

    Crop & Confirm

    + +
    +
    + Select crop area +
    { + e.preventDefault(); + onDragStart(e); + }} + > +
    { + e.stopPropagation(); + onResizeStart(e); + }} + /> +
    +
    +
    + +
    + + +
    +
    +
    +
    + ); + + return createPortal(dialog, document.body); +} diff --git a/frontend/src/components/search/DbControlPanel.tsx b/frontend/src/components/search/DbControlPanel.tsx new file mode 100644 index 0000000..4cfbb89 --- /dev/null +++ b/frontend/src/components/search/DbControlPanel.tsx @@ -0,0 +1,67 @@ +import { useState } from "react"; +import { USER_FRIENDLY_SERVER_ERROR } from "./searchConfig"; +import { callDbEndpoint } from "./searchUtils"; +import type { DbOp, NotificationState } from "./types"; + +export default function DbControlPanel() { + const [activeOp, setActiveOp] = useState(null); + const [notification, setNotification] = useState(null); + + const handleOp = async (op: "create" | "update") => { + if (activeOp) return; + setActiveOp(op); + setNotification(null); + try { + const msg = await callDbEndpoint(op); + setNotification({ message: msg, type: "success" }); + } catch (e: unknown) { + console.error("Database operation failed:", e); + setNotification({ message: USER_FRIENDLY_SERVER_ERROR, type: "error" }); + } finally { + setActiveOp(null); + } + }; + + return ( +
    +
    + + Vector Database +
    + +
    + + + +
    + + {notification && ( +
    + {notification.message} +
    + )} +
    + ); +} diff --git a/frontend/src/components/search/ImagePreview.tsx b/frontend/src/components/search/ImagePreview.tsx new file mode 100644 index 0000000..d83a252 --- /dev/null +++ b/frontend/src/components/search/ImagePreview.tsx @@ -0,0 +1,114 @@ +import CategoryPicker from "./CategoryPicker"; +import LimitSlider from "./LimitSlider"; + +interface ImagePreviewProps { + originalUrl: string; + croppedUrl: string | null; + searchLimit: number; + selectedCategories: string[]; + loading: boolean; + onClear: () => void; + onRecrop: () => void; + onSetCategories: (cats: string[]) => void; + onSetLimit: (v: number) => void; + onSearch: () => void; +} + +export default function ImagePreview({ + originalUrl, + croppedUrl, + searchLimit, + selectedCategories, + loading, + onClear, + onRecrop, + onSetCategories, + onSetLimit, + onSearch, +}: ImagePreviewProps) { + return ( +
    +
    +
    +
    +

    Original Image

    +
    + +
    + Original fabric +
    + + +
    +
    +
    +
    + + {croppedUrl && ( + <> +
    +
    +
    +

    Cropped Image

    +
    +
    + Cropped fabric +
    +
    +
    + +
    + +
    + +
    + + +
    + + )} +
    + ); +} diff --git a/frontend/src/components/search/Lightbox.tsx b/frontend/src/components/search/Lightbox.tsx new file mode 100644 index 0000000..0bb6d9f --- /dev/null +++ b/frontend/src/components/search/Lightbox.tsx @@ -0,0 +1,102 @@ +import type { MouseEventHandler, WheelEventHandler } from "react"; + +interface LightboxProps { + src: string; + caption: string | null; + scale: number; + offset: { x: number; y: number }; + onClose: () => void; + onWheel: WheelEventHandler; + onMouseDown: MouseEventHandler; + onMouseMove: MouseEventHandler; + onMouseUp: MouseEventHandler; + onZoomIn: () => void; + onZoomOut: () => void; + onReset: () => void; +} + +export default function Lightbox({ + src, caption, scale, offset, + onClose, onWheel, onMouseDown, onMouseMove, onMouseUp, + onZoomIn, onZoomOut, onReset, +}: LightboxProps) { + const canZoomOut = scale > 0.51; + const canZoomIn = scale < 5.99; + + return ( +
    { + if ((e.target as HTMLElement).classList.contains("lightbox")) onClose(); + }} + > +
    + {caption + + {caption &&
    {caption}
    } + +
    e.stopPropagation()} + onClick={(e) => e.stopPropagation()} + > + + + + +
    +
    +
    + ); +} diff --git a/frontend/src/components/search/LimitSlider.tsx b/frontend/src/components/search/LimitSlider.tsx new file mode 100644 index 0000000..712a2c7 --- /dev/null +++ b/frontend/src/components/search/LimitSlider.tsx @@ -0,0 +1,27 @@ +interface LimitSliderProps { + value: number; + onChange: (v: number) => void; + label?: string; +} + +export default function LimitSlider({ value, onChange, label = "Results" }: LimitSliderProps) { + return ( +
    + {label} +
    +
    + onChange(Number(e.target.value))} + /> +
    + {value} +
    + ); +} diff --git a/frontend/src/components/search/Pagination.tsx b/frontend/src/components/search/Pagination.tsx new file mode 100644 index 0000000..2c3cb5f --- /dev/null +++ b/frontend/src/components/search/Pagination.tsx @@ -0,0 +1,21 @@ +interface PaginationProps { + page: number; + totalPages: number; + onPrev: () => void; + onNext: () => void; + variant?: "inline" | "bottom"; +} + +export default function Pagination({ page, totalPages, onPrev, onNext, variant = "bottom" }: PaginationProps) { + return ( +
    + + Page {page} / {totalPages} + +
    + ); +} diff --git a/frontend/src/components/search/ResultCard.tsx b/frontend/src/components/search/ResultCard.tsx new file mode 100644 index 0000000..2ee8cb8 --- /dev/null +++ b/frontend/src/components/search/ResultCard.tsx @@ -0,0 +1,75 @@ +import { useState } from "react"; +import type { ResultItem } from "./types"; +import { cleanName, toCdnUrl } from "./searchUtils"; + +interface ResultCardProps { + item: ResultItem; + index: number; + onZoom: (src: string, caption: string) => void; +} + +export default function ResultCard({ item, index, onZoom }: ResultCardProps) { + const [imageFailed, setImageFailed] = useState(false); + + const handleClick = () => { + if (!item.imageSrc || imageFailed) return; + onZoom(toCdnUrl(item.imageSrc), cleanName(item.filename)); + }; + + return ( +
    +
    + {imageFailed ? ( +
    + + Preview unavailable +
    + ) : ( + <> + {item.filename} setImageFailed(true)} + onClick={handleClick} + /> + + + )} +
    + + + + {item.audioSrc && ( +
    +
    + )} +
    + ); +} diff --git a/frontend/src/components/search/ResultsSection.tsx b/frontend/src/components/search/ResultsSection.tsx new file mode 100644 index 0000000..e2b63f8 --- /dev/null +++ b/frontend/src/components/search/ResultsSection.tsx @@ -0,0 +1,46 @@ +import Pagination from "./Pagination"; +import ResultCard from "./ResultCard"; +import type { ResultItem } from "./types"; + +interface ResultsSectionProps { + results: ResultItem[]; + paginatedResults: ResultItem[]; + page: number; + totalPages: number; + selectedCategories: string[]; + isTextSearch: boolean; + onSetCategories: (cats: string[]) => void; + onPrev: () => void; + onNext: () => void; + onZoom: (src: string, caption: string) => void; +} + +export default function ResultsSection({ + results, + paginatedResults, + page, + totalPages, + onPrev, + onNext, + onZoom, +}: ResultsSectionProps) { + return ( +
    +
    +
    {results.length} results
    + +
    + +
    + {paginatedResults.map((item, idx: number) => ( + + ))} +
    +
    + ); +} diff --git a/frontend/src/components/search/SearchHero.tsx b/frontend/src/components/search/SearchHero.tsx new file mode 100644 index 0000000..cc05abe --- /dev/null +++ b/frontend/src/components/search/SearchHero.tsx @@ -0,0 +1,103 @@ +import type { ChangeEvent } from "react"; +import CategoryPicker from "./CategoryPicker"; +import LimitSlider from "./LimitSlider"; + +interface HeroProps { + textQuery: string; + setTextQuery: (v: string) => void; + onTextSearch: () => void; + onClear?: () => void; + showClear?: boolean; + onFileChange: (e: ChangeEvent) => void; + fileInputId: string; + searchLimit: number; + onSetLimit: (v: number) => void; + selectedCategories: string[]; + onSetCategories: (cats: string[]) => void; + loading: boolean; +} + +export default function SearchHero({ + textQuery, setTextQuery, onTextSearch, + onClear, showClear = false, + onFileChange, fileInputId, + searchLimit, onSetLimit, + selectedCategories, onSetCategories, + loading, +}: HeroProps) { + return ( +
    +
    +
    Fabric Intelligence
    + +

    + Find the clothing +
    + + you couldn't find. + +

    + +

    + Visual & semantic search — powered by vectors +

    +
    + +
    +
    + setTextQuery(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && onTextSearch()} + /> + + {showClear && onClear && ( + + )} +
    + + + +
    or
    + + + +
    + +
    + +
    +
    + ); +} diff --git a/frontend/src/components/search/SettingsPanel.test.tsx b/frontend/src/components/search/SettingsPanel.test.tsx new file mode 100644 index 0000000..6ba4906 --- /dev/null +++ b/frontend/src/components/search/SettingsPanel.test.tsx @@ -0,0 +1,15 @@ +import { renderToStaticMarkup } from "react-dom/server"; +import { describe, expect, it } from "vitest"; +import SettingsPanel from "./SettingsPanel"; + +describe("Search settings trigger", () => { + it("renders an accessible icon-only button", () => { + const html = renderToStaticMarkup(); + + expect(html).toContain('aria-label="Open search settings"'); + expect(html).toContain('title="Open search settings"'); + expect(html).toContain("settings-panel__icon"); + expect(html).not.toContain("settings-panel__chevron"); + expect(html).not.toContain(" Settings"); + }); +}); diff --git a/frontend/src/components/search/SettingsPanel.tsx b/frontend/src/components/search/SettingsPanel.tsx new file mode 100644 index 0000000..b9c9446 --- /dev/null +++ b/frontend/src/components/search/SettingsPanel.tsx @@ -0,0 +1,35 @@ +import { useState } from "react"; +import { FiSettings } from "react-icons/fi"; +import DbControlPanel from "./DbControlPanel"; + +export default function SettingsPanel() { + const [open, setOpen] = useState(false); + const accessibleLabel = open + ? "Close search settings" + : "Open search settings"; + + return ( +
    + + + {open && ( +
    + +
    + )} +
    + ); +} diff --git a/frontend/src/components/search/StickySearchBar.tsx b/frontend/src/components/search/StickySearchBar.tsx new file mode 100644 index 0000000..2178807 --- /dev/null +++ b/frontend/src/components/search/StickySearchBar.tsx @@ -0,0 +1,114 @@ +import type { ChangeEvent } from "react"; +import CategoryPicker from "./CategoryPicker"; +import LimitSlider from "./LimitSlider"; + +interface StickySearchBarProps { + textQuery: string; + setTextQuery: (v: string) => void; + onSearch: () => void; + onClear: () => void; + loading: boolean; + isImageMode: boolean; + previewUrl: string | null; + onRecrop: () => void; + fileInputId: string; + onFileChange: (e: ChangeEvent) => void; + selectedCategories: string[]; + onSetCategories: (cats: string[]) => void; + searchLimit: number; + onSetLimit: (v: number) => void; +} + +export default function StickySearchBar({ + textQuery, + setTextQuery, + onSearch, + onClear, + loading, + isImageMode, + previewUrl, + onRecrop, + fileInputId, + onFileChange, + selectedCategories, + onSetCategories, + searchLimit, + onSetLimit, +}: StickySearchBarProps) { + return ( +
    +
    + {isImageMode && previewUrl ? ( +
    +
    + query +
    + +
    + + + + + +
    +
    + ) : ( +
    +
    + setTextQuery(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && onSearch()} + /> +
    + + +
    + )} + + +
    + +
    + + + +
    +
    + ); +} diff --git a/frontend/src/components/search/searchConfig.ts b/frontend/src/components/search/searchConfig.ts new file mode 100644 index 0000000..0c794a8 --- /dev/null +++ b/frontend/src/components/search/searchConfig.ts @@ -0,0 +1,15 @@ +import { BASE_URL, FULL_API_URL } from "../../constants"; + +export const MIN_CROP_SIZE = 40; + +export const CATEGORIES = [ + { id: "stock", label: "Stock", icon: "📦" }, + { id: "fabric", label: "Fabric", icon: "🧵" }, + { id: "design", label: "Design", icon: "🎨" }, + { id: "product", label: "Product", icon: "🖼️" }, +]; + +export const API_BASE = FULL_API_URL; +export const ASSET_BASE = BASE_URL; +export const CDN_BASE = (import.meta.env.VITE_AWS_PUBLIC_URL ?? "https://cdn.threadzip.com").replace(/\/$/, ""); +export const USER_FRIENDLY_SERVER_ERROR = "Unable to connect to the server, please try after some time."; diff --git a/frontend/src/components/search/searchUtils.test.ts b/frontend/src/components/search/searchUtils.test.ts new file mode 100644 index 0000000..68d31f9 --- /dev/null +++ b/frontend/src/components/search/searchUtils.test.ts @@ -0,0 +1,112 @@ +import { describe, expect, it } from "vitest"; +import { + clampCropRectToBounds, + getSearchLayoutState, + getAllImageSearchCategories, + shouldRetryEmptyImageSearch, + toCdnUrl, + toResultItem, +} from "./searchUtils"; + +describe("search response normalization", () => { + it("normalizes legacy string results", () => { + expect(toResultItem("uploads/fabric-blue.jpg")).toMatchObject({ + filename: "fabric-blue.jpg", + }); + expect(toResultItem("uploads/fabric-blue.jpg")?.imageSrc).toContain( + "/images/uploads/fabric-blue.jpg", + ); + }); + + it("normalizes object results and nested backend metadata", () => { + expect( + toResultItem({ + metadata: { + image_url: "/assets/images/sample.webp", + filename: "sample.webp", + audio_url: "/assets/audios/sample.wav", + }, + }), + ).toEqual({ + imageSrc: "/assets/images/sample.webp", + filename: "sample.webp", + audioSrc: "/assets/audios/sample.wav", + }); + }); + + it("rejects malformed results instead of rendering broken cards", () => { + expect(toResultItem(null)).toBeNull(); + expect(toResultItem({ score: 0.9 })).toBeNull(); + }); + + it("does not rewrite absolute, blob, or data URLs", () => { + expect(toCdnUrl("https://example.test/fabric.jpg")).toBe( + "https://example.test/fabric.jpg", + ); + expect(toCdnUrl("blob:https://example.test/id")).toBe( + "blob:https://example.test/id", + ); + }); +}); + +describe("crop bounds", () => { + it("keeps the crop inside the source image", () => { + expect( + clampCropRectToBounds({ x: -10, y: 90, w: 120, h: 80 }, 100, 100), + ).toEqual({ x: 0, y: 20, w: 100, h: 80 }); + }); +}); + +describe("search layout state", () => { + it("keeps the full Search workspace visible for text results", () => { + expect( + getSearchLayoutState({ + hasResults: true, + loading: false, + hasFile: false, + drawerOpen: false, + isTextSearch: true, + }), + ).toMatchObject({ + keepTextSearchWorkspace: true, + showHero: true, + showStickyBar: false, + showImagePreview: false, + }); + }); + + it("retains the compact sticky toolbar for image results", () => { + expect( + getSearchLayoutState({ + hasResults: true, + loading: false, + hasFile: true, + drawerOpen: false, + isTextSearch: false, + }), + ).toMatchObject({ + keepTextSearchWorkspace: false, + showHero: false, + showStickyBar: true, + showImagePreview: false, + }); + }); +}); + +describe("image search categories", () => { + it("provides every category for an empty image-search fallback", () => { + expect(getAllImageSearchCategories()).toEqual([ + "stock", + "fabric", + "design", + "product", + ]); + }); + + it("retries only empty unfiltered image searches", () => { + expect(shouldRetryEmptyImageSearch(undefined, 0)).toBe(true); + expect(shouldRetryEmptyImageSearch([], 0)).toBe(true); + expect(shouldRetryEmptyImageSearch(["fabric"], 0)).toBe(false); + expect(shouldRetryEmptyImageSearch(undefined, 2)).toBe(false); + }); +}); diff --git a/frontend/src/components/search/searchUtils.ts b/frontend/src/components/search/searchUtils.ts new file mode 100644 index 0000000..06c6b35 --- /dev/null +++ b/frontend/src/components/search/searchUtils.ts @@ -0,0 +1,216 @@ +import type { CropRect, ResultItem } from "./types"; +import { ensureOk, fetchWithTimeout } from "../../utils/http"; +import { + API_BASE, + ASSET_BASE, + CATEGORIES, + CDN_BASE, + MIN_CROP_SIZE, +} from "./searchConfig"; + +export function getAllImageSearchCategories(): string[] { + return CATEGORIES.map((category) => category.id); +} + +export function shouldRetryEmptyImageSearch( + categories: string[] | undefined, + resultCount: number, +): boolean { + return (!categories || categories.length === 0) && resultCount === 0; +} + +export interface SearchLayoutStateInput { + hasResults: boolean; + loading: boolean; + hasFile: boolean; + drawerOpen: boolean; + isTextSearch: boolean; +} + +export function getSearchLayoutState({ + hasResults, + loading, + hasFile, + drawerOpen, + isTextSearch, +}: SearchLayoutStateInput) { + const keepTextSearchWorkspace = isTextSearch && !hasFile && !drawerOpen; + + return { + keepTextSearchWorkspace, + showHero: + !hasFile && + !drawerOpen && + (keepTextSearchWorkspace || (!hasResults && !loading)), + showStickyBar: + !keepTextSearchWorkspace && (hasResults || (loading && hasFile)), + showImagePreview: hasFile && !drawerOpen && !hasResults && !loading, + }; +} + +export function clampCropRectToBounds( + rect: CropRect, + imgWidth: number, + imgHeight: number, +): CropRect { + if (imgWidth <= 0 || imgHeight <= 0) return rect; + + const maxW = Math.max(MIN_CROP_SIZE, imgWidth); + const maxH = Math.max(MIN_CROP_SIZE, imgHeight); + const w = Math.min(Math.max(MIN_CROP_SIZE, rect.w), maxW); + const h = Math.min(Math.max(MIN_CROP_SIZE, rect.h), maxH); + const x = Math.min(Math.max(0, rect.x), Math.max(0, imgWidth - w)); + const y = Math.min(Math.max(0, rect.y), Math.max(0, imgHeight - h)); + + return { + x: Math.round(x), + y: Math.round(y), + w: Math.round(w), + h: Math.round(h), + }; +} + +export function toCdnUrl(src: string | undefined): string { + if (!src) return ""; + + const clean = String(src).trim(); + + if (/^(https?:|blob:|data:)/i.test(clean)) { + return clean; + } + + if ( + clean.startsWith("/api/") || + clean.startsWith("/assets/") || + clean.startsWith("/static/") + ) { + return `${ASSET_BASE}${clean}`; + } + + const normalized = clean.replace(/^\/+/, ""); + + if (normalized.startsWith("images/") || normalized.startsWith("audios/")) { + return `${CDN_BASE}/${normalized}`; + } + + return `${CDN_BASE}/images/${normalized}`; +} + +export function toResultItem(raw: unknown): ResultItem | null { + if (typeof raw === "string") { + const imageSrc = toCdnUrl(raw); + if (!imageSrc) return null; + return { + imageSrc, + filename: raw.split(/[?#]/)[0].split("/").pop() ?? raw, + }; + } + + if (!raw || typeof raw !== "object") return null; + + const record = raw as Record; + const metadata = + record.metadata && typeof record.metadata === "object" + ? (record.metadata as Record) + : {}; + + const pickString = (...values: unknown[]): string | undefined => + values.find( + (value): value is string => + typeof value === "string" && value.trim().length > 0, + ); + + const imageValue = pickString( + record.imageSrc, + record.imageUrl, + record.image_url, + record.url, + record.path, + record.relPath, + metadata.imageSrc, + metadata.imageUrl, + metadata.image_url, + metadata.url, + metadata.imagePath, + metadata.relPath, + metadata.filename, + ); + if (!imageValue) return null; + + const audioValue = pickString( + record.audioSrc, + record.audioUrl, + record.audio_url, + metadata.audioSrc, + metadata.audioUrl, + metadata.audio_url, + metadata.audioPath, + metadata.audioRelPath, + ); + const filename = pickString( + record.filename, + record.basename, + metadata.imageFilename, + metadata.filename, + metadata.basename, + imageValue.split(/[?#]/)[0].split("/").pop(), + ); + + return { + imageSrc: toCdnUrl(imageValue), + filename: filename ?? "Fabric sample", + audioSrc: audioValue ? toCdnUrl(audioValue) : undefined, + }; +} + +export function cleanName(filename: string): string { + if (!filename) return "Fabric sample"; + + const raw = decodeURIComponent( + String(filename).split(/[?#]/)[0].split("/").pop() ?? filename, + ).trim(); + const withoutExtension = raw.replace(/\.[^.]+$/, ""); + + const normalized = withoutExtension + // Drop backend storage timestamps / IDs, but keep the human filename prefix. + .replace(/(?:[_\s-]?20\d{6}T\d{6})(?:[_\s-]?[a-z0-9]{4,})?$/i, "") + .replace(/(?:[_\s-]?20\d{6})[_\s-]?\d{6}.*$/i, "") + .replace(/[_\s-][a-f0-9]{5,}$/i, "") + .replace(/\b\d{10,}\b/g, "") + .replace(/^[a-f0-9]{6,}\s+/i, "") + .replace(/^\d+(?:[\s_-]+\d+){1,}\s*/i, "") + .replace(/[_-](?:copy|final|submitted)$/i, "") + .replace(/[_-]+/g, " ") + .replace(/\s+/g, " ") + .trim(); + + const letters = (normalized.match(/[a-z]/gi) ?? []).length; + const digits = (normalized.match(/\d/g) ?? []).length; + const looksTechnical = + !letters || + digits > letters || + /\b(?:single image|image|img|upload|submitted files?)\b/i.test( + normalized, + ) || + /^[a-f0-9]{6,}\b/i.test(normalized); + + if (looksTechnical) return "Fabric sample"; + + const shortName = normalized.split(" ").slice(0, 3).join(" "); + return ( + shortName.replace(/\b\w/g, (char) => char.toUpperCase()) || "Fabric sample" + ); +} + +export async function callDbEndpoint(op: "create" | "update"): Promise { + const url = + op === "create" + ? `${API_BASE}/database/create/table` + : `${API_BASE}/database/update/table`; + const response = await fetchWithTimeout(url, { method: "PUT" }, 30_000); + await ensureOk(response, `Request failed (${response.status}).`); + const data = (await response.json().catch(() => ({}))) as { + message?: unknown; + }; + return typeof data.message === "string" ? data.message : "Done."; +} diff --git a/frontend/src/components/search/types.ts b/frontend/src/components/search/types.ts new file mode 100644 index 0000000..c967066 --- /dev/null +++ b/frontend/src/components/search/types.ts @@ -0,0 +1,26 @@ +export type NotificationState = { + message: string; + type: "success" | "error"; +} | null; +export type DbOp = "create" | "update" | null; + +export interface ResultItem { + imageSrc: string; + filename: string; + audioSrc?: string; +} + +export interface SearchApiResponse { + message?: string; + results?: unknown[]; + pagination?: { + page?: number; + per_page?: number; + total_results?: number; + total_pages?: number; + has_next?: boolean; + has_prev?: boolean; + }; +} + +export type CropRect = { x: number; y: number; w: number; h: number }; diff --git a/frontend/src/components/search/useSearch.ts b/frontend/src/components/search/useSearch.ts new file mode 100644 index 0000000..7891037 --- /dev/null +++ b/frontend/src/components/search/useSearch.ts @@ -0,0 +1,169 @@ +import { useCallback, useEffect, useRef, useState } from "react"; +import { + ensureOk, + fetchWithTimeout, + toUserFacingNetworkError, +} from "../../utils/http"; +import { API_BASE, USER_FRIENDLY_SERVER_ERROR } from "./searchConfig"; +import { + getAllImageSearchCategories, + shouldRetryEmptyImageSearch, + toResultItem, +} from "./searchUtils"; +import type { ResultItem, SearchApiResponse } from "./types"; + +const SEARCH_TIMEOUT_MS = 45_000; + +type SearchInput = + | { kind: "image"; file: File } + | { kind: "text"; term: string }; + +export function useSearch() { + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [results, setResults] = useState([]); + const requestRef = useRef(null); + const requestIdRef = useRef(0); + + useEffect( + () => () => { + requestRef.current?.abort(); + }, + [], + ); + + const executeSearch = useCallback( + async ( + input: SearchInput, + category?: string[], + limit = 20, + preserveResultsOnError = false, + ) => { + console.log(input); + requestRef.current?.abort(); + const controller = new AbortController(); + requestRef.current = controller; + const requestId = ++requestIdRef.current; + + setLoading(true); + setError(null); + + try { + const requestResults = async ( + categories?: string[], + ): Promise => { + const form = new FormData(); + if (input.kind === "image") form.append("file", input.file); + else form.append("search_term", input.term); + form.append("limit", String(limit)); + form.append("page", "1"); + form.append("per_page", String(limit)); + categories?.forEach((value) => { + form.append("category", value); + }); + + const response = await fetchWithTimeout( + `${API_BASE}/search`, + { + method: "POST", + body: form, + signal: controller.signal, + }, + SEARCH_TIMEOUT_MS, + ); + await ensureOk(response, `Search failed (${response.status}).`); + + const data = (await response.json()) as SearchApiResponse; + const rawResults = Array.isArray(data.results) ? data.results : []; + const normalized = rawResults + .map(toResultItem) + .filter((item): item is ResultItem => item !== null); + + if (rawResults.length > 0 && normalized.length === 0) { + throw new Error( + "The search service returned an unsupported result format.", + ); + } + + return normalized; + }; + + let normalized = await requestResults(category); + + // Some vector indexes only return tagged rows once a category predicate + // is present. Preserve the normal unfiltered request, but recover from an + // empty image response by retrying once across every visible category. + if ( + input.kind === "image" && + shouldRetryEmptyImageSearch(category, normalized.length) && + !controller.signal.aborted + ) { + normalized = await requestResults(getAllImageSearchCategories()); + } + + if (requestId === requestIdRef.current) setResults(normalized); + } catch (caught) { + if (caught instanceof DOMException && caught.name === "AbortError") + return; + + const failure = toUserFacingNetworkError( + caught, + USER_FRIENDLY_SERVER_ERROR, + ); + if (requestId === requestIdRef.current) { + setError(failure.message); + if (!preserveResultsOnError) setResults([]); + } + } finally { + if (requestId === requestIdRef.current) { + setLoading(false); + requestRef.current = null; + } + } + }, + [], + ); + + const runImageSearch = useCallback( + ( + file: File, + category?: string[], + limit = 20, + preserveResultsOnError = false, + ) => + executeSearch( + { kind: "image", file }, + category, + limit, + preserveResultsOnError, + ), + [executeSearch], + ); + + const runTextSearch = useCallback( + ( + term: string, + category?: string[], + limit = 20, + preserveResultsOnError = false, + ) => + executeSearch( + { kind: "text", term }, + category, + limit, + preserveResultsOnError, + ), + [executeSearch], + ); + + const clear = useCallback(() => { + requestRef.current?.abort(); + requestRef.current = null; + requestIdRef.current += 1; + setLoading(false); + setResults([]); + setError(null); + }, []); + + return { loading, error, results, runImageSearch, runTextSearch, clear }; +} diff --git a/frontend/src/hooks/chat.ts b/frontend/src/hooks/chat.ts index 8f2f7fb..4179a6f 100644 --- a/frontend/src/hooks/chat.ts +++ b/frontend/src/hooks/chat.ts @@ -7,6 +7,7 @@ import { } from "../services/chat_api"; import { FULL_API_URL } from "../constants"; import { extractFilenameFromText } from "../utils/extractFilenameFromText"; +import { fetchWithTimeout } from "../utils/http"; type Status = "idle" | "sending" | "error" | "validating"; @@ -84,13 +85,10 @@ const shouldOfferMoreForQuestion = (content: unknown): boolean => { const isFabricQuestion = /\bfabricai\b/.test(normalized); const hasHowIntent = /\bhow\b/.test(normalized); const hasUseIntent = - /\b(use|using|work with|start|begin|access|operate)\b/.test( - normalized, - ) || /\bget started\b/.test(normalized); + /\b(use|using|work with|start|begin|access|operate)\b/.test(normalized) || + /\bget started\b/.test(normalized); const hasHelpIntent = - /\b(guide|tutorial|help|steps|instructions|walkthrough)\b/.test( - normalized, - ); + /\b(guide|tutorial|help|steps|instructions|walkthrough)\b/.test(normalized); return isFabricQuestion && ((hasHowIntent && hasUseIntent) || hasHelpIntent); }; @@ -238,15 +236,14 @@ export default function useChat() { form.append("image", file); const tForm = performance.now(); - const ac = new AbortController(); - const timeout = setTimeout(() => ac.abort(), 30000); - - const resp = await fetch(`${FULL_API_URL}/validate-image`, { - method: "POST", - body: form, - signal: ac.signal, - }); - clearTimeout(timeout); + const resp = await fetchWithTimeout( + `${FULL_API_URL}/validate-image`, + { + method: "POST", + body: form, + }, + 30_000, + ); const tResp = performance.now(); if (!resp.ok) { @@ -305,7 +302,7 @@ export default function useChat() { if (uploadedPreviewUrl) { try { URL.revokeObjectURL(uploadedPreviewUrl); - } catch {} + } catch { /* Best-effort cleanup or browser storage operation. */ } } setStatus("validating"); setError(""); @@ -665,15 +662,15 @@ export default function useChat() { return obj.response.response; if (typeof obj.text === "string") return obj.text; if (typeof obj.message === "string") return obj.message; - } catch {} + } catch { /* Best-effort cleanup or browser storage operation. */ } } return raw; }; try { - createAbort(); + const signal = createAbort(); const regenChatRes = await withAbort( - chatOnce([...messages, { role: "user", content: instr }]), + chatOnce([...messages, { role: "user", content: instr }], { signal }), ); const rc = regenChatRes as RichChatResponse; @@ -722,7 +719,7 @@ export default function useChat() { return null; } try { - createAbort(); + const signal = createAbort(); setStatus("sending"); setError(""); @@ -732,10 +729,15 @@ export default function useChat() { form.append("image", uploadedImageFile); const upResp = await withAbort( - fetch(`${FULL_API_URL}/uploads/tmp_media`, { - method: "POST", - body: form, - }), + fetchWithTimeout( + `${FULL_API_URL}/uploads/tmp_media`, + { + method: "POST", + body: form, + signal, + }, + 60_000, + ), ); if (!upResp.ok) { @@ -755,11 +757,16 @@ export default function useChat() { }; const searchResp = await withAbort( - fetch(`${FULL_API_URL}/search`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(args), - }), + fetchWithTimeout( + `${FULL_API_URL}/search`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(args), + signal, + }, + 60_000, + ), ); if (!searchResp.ok) @@ -777,7 +784,7 @@ export default function useChat() { try { localStorage.setItem("mcp_last_search", JSON.stringify(payload)); - } catch {} + } catch { /* Best-effort cleanup or browser storage operation. */ } const qs = new URLSearchParams({ k: String(payload.k) }).toString(); @@ -794,7 +801,10 @@ export default function useChat() { const searchInstruction = `Search similar images: image_url=${imageUrl} k=${k} min_sim=${min_sim} order=recent require_audio=false`; const chatRes = await withAbort( - chatOnce([...messages, { role: "user", content: searchInstruction }]), + chatOnce( + [...messages, { role: "user", content: searchInstruction }], + { signal }, + ), ); const rc = chatRes as RichChatResponse; @@ -812,7 +822,7 @@ export default function useChat() { try { localStorage.setItem("mcp_last_search", JSON.stringify(payload)); - } catch {} + } catch { /* Best-effort cleanup or browser storage operation. */ } const qs = new URLSearchParams({ k: String(payload.k), @@ -902,7 +912,7 @@ export default function useChat() { } } - createAbort(); + const signal = createAbort(); setStatus("sending"); setError(""); @@ -959,13 +969,18 @@ export default function useChat() { else debug[k] = String(v); } console.log("[UPLOAD DEBUG]", debug); - } catch {} + } catch { /* Best-effort cleanup or browser storage operation. */ } const upResp = await withAbort( - fetch(`${FULL_API_URL}/uploads/tmp_media`, { - method: "POST", - body: form, - }), + fetchWithTimeout( + `${FULL_API_URL}/uploads/tmp_media`, + { + method: "POST", + body: form, + signal, + }, + 60_000, + ), ); if (!upResp.ok) { const t = await upResp.text().catch(() => ""); @@ -1016,10 +1031,10 @@ export default function useChat() { } const chatRes = await withAbort( - chatOnce([ - ...messages, - { role: "user", content: mediaInstruction }, - ]), + chatOnce( + [...messages, { role: "user", content: mediaInstruction }], + { signal }, + ), ); const rc = chatRes as RichChatResponse; @@ -1062,7 +1077,10 @@ export default function useChat() { } const chatRes = await withAbort( - chatOnce([...messages, { role: "user", content: text }]), + chatOnce([...messages, { role: "user", content: text }], { + retryTransient: true, + signal, + }), ); handleResponse(chatRes); setStatus("idle"); @@ -1099,7 +1117,7 @@ export default function useChat() { const onAssistantRendered = useCallback( (lastAssistant: Message) => { try { - if (!lastAssistant || !lastAssistant.content) return; + if (!lastAssistant?.content) return; const content = String(lastAssistant.content).trim(); // If we have a pending ask-more token and the rendered content matches it, @@ -1172,11 +1190,11 @@ export default function useChat() { const retryLast = useCallback(async () => { if (status === "sending" || messages.length === 0) return; - createAbort(); + const signal = createAbort(); setStatus("sending"); setError(""); try { - const res = await withAbort(chatOnce(messages)); + const res = await withAbort(chatOnce(messages, { signal })); handleResponse(res); setStatus("idle"); } catch (e: unknown) { @@ -1196,7 +1214,7 @@ export default function useChat() { if (abortRef.current) { try { abortRef.current.abort(); - } catch {} + } catch { /* Best-effort cleanup or browser storage operation. */ } abortRef.current = null; } setMessages([]); diff --git a/frontend/src/hooks/useImageAnalysis.ts b/frontend/src/hooks/useImageAnalysis.ts index 121da26..3bf0b19 100644 --- a/frontend/src/hooks/useImageAnalysis.ts +++ b/frontend/src/hooks/useImageAnalysis.ts @@ -6,9 +6,20 @@ import { validateImageAPI, } from "../services/analyze_api.ts"; import { fetchImageAsFile } from "../utils/image-helper.ts"; +import { + ANALYSIS_RESPONSE_COUNT, + toBackendResponseIndex, +} from "../utils/analysisResponseIndex"; type Mode = "short" | "long"; +const MOBILE_DRAWER_QUERY = "(max-width: 980px)"; + +const getInitialDrawerState = (): boolean => + typeof window !== "undefined" && + typeof window.matchMedia === "function" && + !window.matchMedia(MOBILE_DRAWER_QUERY).matches; + const extractFilename = (path: string, fallback = "fabric.jpg"): string => { const base = path.split(/[?#]/)[0]; // strip query/hash const seg = base.split("/").filter(Boolean).pop(); @@ -27,7 +38,7 @@ const useImageAnalysis = () => { const [showUploadedImage, setShowUploadedImage] = useState(false); const [uploadedImageUrl, setUploadedImageUrl] = useState(null); const [sampleImageUrl, setSampleImageUrl] = useState(null); - const [showDrawer, setShowDrawer] = useState(true); + const [showDrawer, setShowDrawer] = useState(getInitialDrawerState); const [typedText, setTypedText] = useState(""); const [isValidImage, setIsValidImage] = useState(null); const [validationLoading, setValidationLoading] = useState(false); @@ -41,6 +52,26 @@ const useImageAnalysis = () => { const latestRunIdRef = useRef(0); const objectUrlsRef = useRef>(new Set()); + useEffect(() => { + if (typeof window.matchMedia !== "function") return; + + const mobileQuery = window.matchMedia(MOBILE_DRAWER_QUERY); + const closeDrawerOnMobile = (event: MediaQueryListEvent | MediaQueryList) => { + if (event.matches) setShowDrawer(false); + }; + + closeDrawerOnMobile(mobileQuery); + + if (typeof mobileQuery.addEventListener === "function") { + mobileQuery.addEventListener("change", closeDrawerOnMobile); + return () => + mobileQuery.removeEventListener("change", closeDrawerOnMobile); + } + + mobileQuery.addListener(closeDrawerOnMobile); + return () => mobileQuery.removeListener(closeDrawerOnMobile); + }, []); + const createTrackedObjectUrl = useCallback((file: File) => { const url = URL.createObjectURL(file); objectUrlsRef.current.add(url); @@ -100,7 +131,7 @@ const useImageAnalysis = () => { const firstObject = response.response; const first = (firstObject?.response as string) ?? ""; - const allResponses = Array(6).fill(""); + const allResponses = Array(ANALYSIS_RESPONSE_COUNT).fill(""); allResponses[0] = first; setResponses(allResponses); @@ -198,7 +229,7 @@ const useImageAnalysis = () => { const firstObject = response.response; const first = (firstObject?.response as string) ?? ""; - const allResponses = Array(6).fill(""); + const allResponses = Array(ANALYSIS_RESPONSE_COUNT).fill(""); allResponses[0] = first; setResponses(allResponses); @@ -300,7 +331,9 @@ const useImageAnalysis = () => { const handleNext = async () => { const newIndex = currentIndex + 1; - // Use cached response if available + if (newIndex >= ANALYSIS_RESPONSE_COUNT) return; + + // Use cached response if available. if (responses[newIndex]) { setCurrentIndex(newIndex); setDescription(responses[newIndex]); @@ -312,30 +345,44 @@ const useImageAnalysis = () => { return; } + setLoading(true); try { - const res = await regenerateResponse(cacheKey, String(newIndex)); - const nextText = (res?.response as string) ?? ""; + // The API stores variants as 1..6 while the UI array is 0..5. The old + // code sent the zero-based UI index, duplicated response 1, and never + // requested backend variation 6. + const backendIndex = String(toBackendResponseIndex(newIndex)); + let res = await regenerateResponse(cacheKey, backendIndex); + let nextText = (res?.response as string) ?? ""; + + // A background variant can finish just after the server's first wait + // window. Retry one time so the final response does not fail at 6/6. + if (!nextText) { + await new Promise((resolve) => setTimeout(resolve, 500)); + res = await regenerateResponse(cacheKey, backendIndex); + nextText = (res?.response as string) ?? ""; + } + if (nextText) { - const updated = [...responses]; - // Ensure array is long enough - if (updated.length <= newIndex) { - updated.length = newIndex + 1; - for (let i = 0; i < updated.length; i++) { - if (typeof updated[i] !== "string") updated[i] = ""; - } - } + const updated = Array.from( + { length: ANALYSIS_RESPONSE_COUNT }, + (_, index) => responses[index] ?? "", + ); updated[newIndex] = nextText; setResponses(updated); setCurrentIndex(newIndex); setDescription(nextText); } else { - setAnalysisPopupMessage("No more responses available."); + setAnalysisPopupMessage( + "This response is still being prepared. Please select Next again.", + ); } } catch (err) { console.error("Next response fetch failed:", err); setAnalysisPopupMessage( - "Unable to generate another response. Please try after some time.", + "Unable to generate another response. Please try again.", ); + } finally { + setLoading(false); } }; @@ -369,15 +416,16 @@ const useImageAnalysis = () => { }; useEffect(() => { + const trackedUrls = objectUrlsRef.current; return () => { - for (const url of objectUrlsRef.current) { + for (const url of trackedUrls) { try { URL.revokeObjectURL(url); } catch { // Ignore stale preview URLs during teardown. } } - objectUrlsRef.current.clear(); + trackedUrls.clear(); }; }, []); diff --git a/frontend/src/hooks/usePageTracking.ts b/frontend/src/hooks/usePageTracking.ts index b241ff7..e06056e 100644 --- a/frontend/src/hooks/usePageTracking.ts +++ b/frontend/src/hooks/usePageTracking.ts @@ -1,12 +1,27 @@ import { useEffect } from "react"; import { useLocation } from "react-router-dom"; -const MEASUREMENT_ID = import.meta.env.VITE_GA_MEASUREMENT_ID || "G-LSPKHRMZZW"; -const TRACKING_DELAY_MS = 1600; +const MEASUREMENT_ID = String( + import.meta.env.VITE_GA_MEASUREMENT_ID ?? "", +).trim(); +const ANALYTICS_ENABLED = + import.meta.env.VITE_ANALYTICS_ENABLED === "true" && !!MEASUREMENT_ID; let analyticsModulePromise: Promise | null = null; let analyticsInitialized = false; +function privacySignalEnabled(): boolean { + if (typeof navigator === "undefined") return false; + + const browser = navigator as Navigator & { globalPrivacyControl?: boolean }; + const browserWindow = window as Window & { doNotTrack?: string }; + return ( + browser.globalPrivacyControl === true || + navigator.doNotTrack === "1" || + browserWindow.doNotTrack === "1" + ); +} + const loadAnalytics = async () => { analyticsModulePromise ??= import("react-ga4"); const { default: ReactGA } = await analyticsModulePromise; @@ -23,29 +38,23 @@ export const usePageTracking = () => { const location = useLocation(); useEffect(() => { - if (import.meta.env.DEV || !MEASUREMENT_ID) { + if (import.meta.env.DEV || !ANALYTICS_ENABLED || privacySignalEnabled()) { return; } - const page = `${location.pathname}${location.search}`; let cancelled = false; + const page = `${location.pathname}${location.search}`; - const timer = window.setTimeout(async () => { - if (cancelled) return; - - try { - const ReactGA = await loadAnalytics(); - if (!cancelled) { - ReactGA.send({ hitType: "pageview", page }); - } - } catch { - // Analytics should never block or fail the UI. - } - }, TRACKING_DELAY_MS); + void loadAnalytics() + .then((ReactGA) => { + if (!cancelled) ReactGA.send({ hitType: "pageview", page }); + }) + .catch(() => { + // Analytics is optional and must never interrupt the product flow. + }); return () => { cancelled = true; - window.clearTimeout(timer); }; }, [location.pathname, location.search]); }; diff --git a/frontend/src/hooks/useRouteMetadata.ts b/frontend/src/hooks/useRouteMetadata.ts new file mode 100644 index 0000000..81b292b --- /dev/null +++ b/frontend/src/hooks/useRouteMetadata.ts @@ -0,0 +1,99 @@ +import { useEffect } from "react"; +import { useLocation } from "react-router-dom"; + +const SITE_NAME = "FabricAI"; +const SITE_ORIGIN = "https://pro.threadzip.com"; + +type RouteMetadata = { + title: string; + description: string; +}; + +const ROUTE_METADATA: Record = { + "/": { + title: "FabricAI | Fabric Analysis and Search", + description: + "Analyze, search, upload, and discuss fabric images with FabricAI tools.", + }, + "/analysis": { + title: "Fabric Analyzer | FabricAI", + description: + "Run short or detailed AI-assisted analysis on a fabric image.", + }, + "/upload": { + title: "Upload Fabric Media | FabricAI", + description: + "Upload fabric images and optional audio notes for processing.", + }, + "/view": { + title: "Uploaded Fabrics | FabricAI", + description: "Review previously uploaded fabric images and audio records.", + }, + "/search": { + title: "Fabric Search | FabricAI", + description: "Search the fabric collection by text or reference image.", + }, + "/chat": { + title: "FabricAI Assistant", + description: "Ask fabric questions and discuss image analysis results.", + }, + "/reader": { + title: "Document Reader | FabricAI", + description: "Select a supported document type for structured extraction.", + }, + "/reader/pan": { + title: "PAN Card Reader | FabricAI", + description: "Extract supported fields from a PAN card image.", + }, + "/reader/aadhaar": { + title: "Aadhaar Card Reader | FabricAI", + description: "Extract supported fields from an Aadhaar card image.", + }, + "/contact": { + title: "Contact | FabricAI", + description: "Contact the FabricAI team.", + }, +}; + +const FALLBACK_METADATA: RouteMetadata = { + title: SITE_NAME, + description: + "AI-assisted tools for fabric analysis, search, and documentation.", +}; + +function setMetaContent(selector: string, content: string) { + const element = document.querySelector(selector); + if (element) element.content = content; +} + +function resolveMetadata(pathname: string): RouteMetadata { + return ( + ROUTE_METADATA[pathname] ?? + (pathname.startsWith("/reader/") ? ROUTE_METADATA["/reader"] : undefined) ?? + FALLBACK_METADATA + ); +} + +export function useRouteMetadata() { + const location = useLocation(); + + useEffect(() => { + const metadata = resolveMetadata(location.pathname); + const canonicalUrl = new URL(location.pathname, SITE_ORIGIN).toString(); + + document.title = metadata.title; + setMetaContent('meta[name="description"]', metadata.description); + setMetaContent('meta[name="title"]', metadata.title); + setMetaContent('meta[property="og:title"]', metadata.title); + setMetaContent('meta[property="og:description"]', metadata.description); + setMetaContent('meta[property="og:url"]', canonicalUrl); + setMetaContent('meta[name="twitter:title"]', metadata.title); + setMetaContent('meta[name="twitter:description"]', metadata.description); + setMetaContent('meta[name="twitter:url"]', canonicalUrl); + + const canonical = document.querySelector( + 'link[rel="canonical"]', + ); + if (canonical) canonical.href = canonicalUrl; + }, [location.pathname]); +} diff --git a/frontend/src/hooks/useUploadAndRecord.ts b/frontend/src/hooks/useUploadAndRecord.ts index 0aec7bd..450a881 100644 --- a/frontend/src/hooks/useUploadAndRecord.ts +++ b/frontend/src/hooks/useUploadAndRecord.ts @@ -1,5 +1,6 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { FULL_API_URL } from "../constants"; +import { fetchWithTimeout } from "../utils/http"; const SUBMIT_SERVER_ERROR = "Unable to connect to the server, please try after some time."; @@ -298,10 +299,14 @@ export const useUploadAndRecord = () => { setLoading(true); setNotification(null); try { - const res = await fetch(`${FULL_API_URL}/submit`, { - method: "POST", - body: formData, - }); + const res = await fetchWithTimeout( + `${FULL_API_URL}/submit`, + { + method: "POST", + body: formData, + }, + 90_000, + ); if (!res.ok) { const data = (await res.json().catch(() => null)) as { diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 35cc9e5..fc5f9b1 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -3,27 +3,28 @@ import ReactDOM from "react-dom/client"; import { BrowserRouter } from "react-router-dom"; import App from "./App"; -import { usePageTracking } from "./hooks/usePageTracking"; import "./index.css"; import "@/assets/styles/_makeover.css"; +import "@/assets/styles/SeniorUiFixes.css"; +import "@/assets/styles/ReaderAnalysisContrastFixes.css"; +import "@/assets/styles/AnalysisUploadButtonParity.css"; +import "@/assets/styles/AnalysisHeaderVisibilityParity.css"; +import "@/assets/styles/HomeViewportNavigationFix.css"; +import "@/assets/styles/CrossRouteLayoutCompletion.css"; +import "@/assets/styles/MobileViewportStability.css"; const rootElement = document.getElementById("root"); -// eslint-disable-next-line react-refresh/only-export-components -const TrackingProvider = ({ children }: { children: React.ReactNode }) => { - usePageTracking(); // This tracks every route change automatically - return <>{children}; -}; - if (rootElement) { + document.documentElement.dataset.appBooted = "true"; + document.getElementById("app-boot-fallback")?.setAttribute("hidden", ""); + ReactDOM.createRoot(rootElement).render( - - - + - + , ); } else { console.error("Root element not found"); diff --git a/frontend/src/pages/AadhaarCardReader.tsx b/frontend/src/pages/AadhaarCardReader.tsx index 575cd42..b37bcbc 100644 --- a/frontend/src/pages/AadhaarCardReader.tsx +++ b/frontend/src/pages/AadhaarCardReader.tsx @@ -4,6 +4,7 @@ import Cropper from "react-easy-crop"; import * as htmlToImage from "html-to-image"; import { FULL_API_URL } from "@/constants"; +import { ensureOk, fetchWithTimeout } from "@/utils/http"; import { useNavigate } from "react-router-dom"; /* ======================= @@ -52,7 +53,6 @@ const AadhaarCardReader = () => { const [croppedImage, setCroppedImage] = useState(null); const [originalPreview, setOriginalPreview] = useState(null); - // react-easy-crop state const [crop, setCrop] = useState({ x: 0, y: 0 }); const [zoom, setZoom] = useState(1); @@ -65,7 +65,7 @@ const AadhaarCardReader = () => { setCroppedImage(null); if (f) { const url = URL.createObjectURL(f); - setOriginalPreview(url); // 🔥 ADD + setOriginalPreview(url); // 🔥 ADD setPreview(url); setShowCropper(true); } @@ -80,20 +80,16 @@ const AadhaarCardReader = () => { } }; - const onCropComplete = useCallback( - (_: Area, croppedPixels: Area) => { - setCroppedAreaPixels(croppedPixels); - }, - [] - ); - + const onCropComplete = useCallback((_: Area, croppedPixels: Area) => { + setCroppedAreaPixels(croppedPixels); + }, []); const createCroppedImage = async (): Promise => { if (!originalPreview || !croppedAreaPixels) return null; return new Promise((resolve) => { const image = new Image(); - image.src = originalPreview; + image.src = originalPreview; image.onload = () => { const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); @@ -111,7 +107,7 @@ const AadhaarCardReader = () => { 0, 0, canvas.width, - canvas.height + canvas.height, ); resolve(canvas.toDataURL("image/jpeg")); @@ -120,7 +116,6 @@ const AadhaarCardReader = () => { }); }; - const handleApplyCrop = async () => { const croppedDataUrl = await createCroppedImage(); if (croppedDataUrl) { @@ -135,7 +130,6 @@ const AadhaarCardReader = () => { setShowCropper(true); }; - const dataURLtoFile = (dataUrl: string, filename: string): File => { const arr = dataUrl.split(","); const mime = arr[0].match(/:(.*?);/)?.[1] || "image/png"; @@ -167,14 +161,15 @@ const AadhaarCardReader = () => { fd.append("side", side); try { - const res = await fetch(`${FULL_API_URL}/adhaar`, { - method: "POST", - body: fd, - }); - - if (!res.ok) { - throw new Error(`HTTP error! status: ${res.status}`); - } + const res = await fetchWithTimeout( + `${FULL_API_URL}/adhaar`, + { + method: "POST", + body: fd, + }, + 60_000, + ); + await ensureOk(res, "Failed to process Aadhaar card."); const data = await res.json(); setResult(data); @@ -209,7 +204,10 @@ const AadhaarCardReader = () => { }; return ( -
    +
    - -
    {error && (
    @@ -246,7 +242,7 @@ const AadhaarCardReader = () => { cursor: "pointer", transform: hovered ? "scale(1.06)" : "scale(1)", boxShadow: hovered - ? "0 12px 40px rgba(255, 107, 53, 0.35)" + ? "var(--document-reader-accent-shadow)" : "none", }} onMouseEnter={() => setHovered(true)} @@ -259,7 +255,7 @@ const AadhaarCardReader = () => { height="48" viewBox="0 0 24 24" fill="none" - stroke="#FF6B35" + stroke="currentColor" strokeWidth="2" > @@ -267,7 +263,9 @@ const AadhaarCardReader = () => {

    Aadhaar Card Reader

    -

    Choose side → upload aadhar card → crop → extract

    +

    + Choose side → upload aadhar card → crop → extract +

    { - Your image is processed only in memory during this session. - We do not store or save images or share data. + Your image is processed only in memory during this session. We do{" "} + not store or save images or share data.
    @@ -333,18 +331,25 @@ const AadhaarCardReader = () => {
    ) : ( @@ -405,7 +410,11 @@ const AadhaarCardReader = () => { {/* CROPPED IMAGE PREVIEW */} {croppedImage && !showCropper && (
    - Cropped Aadhaar + Cropped Aadhaar
    -
    @@ -425,18 +438,22 @@ const AadhaarCardReader = () => { {/* RESULT */} {(loading || result) && ( <> - + {!loading && ( <> - - @@ -460,11 +477,7 @@ type AadhaarPreviewProps = { const SKELETON = "████████"; -const AadhaarCardPreview = ({ - data, - side, - loading, -}: AadhaarPreviewProps) => { +const AadhaarCardPreview = ({ data, side, loading }: AadhaarPreviewProps) => { const timestamp = new Intl.DateTimeFormat("en-IN", { timeZone: "Asia/Kolkata", year: "numeric", @@ -491,9 +504,7 @@ const AadhaarCardPreview = ({ {loading &&
    }
    -
    - {loading ? SKELETON : "AADHAAR CARD"} -
    +
    {loading ? SKELETON : "AADHAAR CARD"}
    @@ -510,10 +521,7 @@ const AadhaarCardPreview = ({ )} {side === "back" && ( - + )}
    @@ -531,7 +539,6 @@ const AadhaarCardPreview = ({ ); }; - const Field = ({ label, value }: { label: string; value?: string }) => (
    {label}
    @@ -545,11 +552,12 @@ const Field = ({ label, value }: { label: string; value?: string }) => ( const styles: Record = { wrapper: { - minHeight: "100vh", + minHeight: 0, + width: "100%", + flex: "1 0 auto", background: "var(--document-reader-page-bg)", padding: "60px 20px", - fontFamily: "'DM Sans', -apple-system, sans-serif", - + fontFamily: "var(--tz-font-body)", }, container: { maxWidth: 520, @@ -557,13 +565,11 @@ const styles: Record = { textAlign: "center", }, title: { - fontFamily: "'Instrument Serif', serif", - fontSize: 48, - fontWeight: 400, + fontFamily: "var(--tz-font-display)", + fontSize: "clamp(2rem, 5vw, 3rem)", + fontWeight: 700, margin: "0 0 12px 0", - background: "var(--document-reader-title-gradient)", - WebkitBackgroundClip: "text", - WebkitTextFillColor: "transparent", + color: "var(--document-reader-text)", letterSpacing: "-0.02em", textAlign: "center", }, @@ -595,23 +601,23 @@ const styles: Record = { transition: "all 0.2s ease", }, sideBtnActive: { - background: "linear-gradient(135deg, #FF6B35 0%, #F7931E 100%)", - color: "var(--document-reader-text)", + background: "var(--document-reader-primary-bg)", + color: "var(--document-reader-accent-contrast)", }, uploadZone: { position: "relative" as const, padding: 60, - border: "2px dashed rgba(255, 107, 53, 0.3)", + border: "2px dashed var(--document-reader-accent-border)", borderRadius: 24, - background: "rgba(255, 107, 53, 0.03)", + background: "var(--document-reader-surface-faint)", cursor: "pointer", textAlign: "center", marginBottom: 20, transition: "all 0.3s ease", }, uploadZoneDragging: { - borderColor: "#FF6B35", - background: "rgba(255, 107, 53, 0.08)", + borderColor: "var(--document-reader-accent)", + background: "var(--document-reader-accent-soft)", transform: "scale(1.02)", }, fileInput: { @@ -622,7 +628,7 @@ const styles: Record = { display: "block", }, uploadIcon: { - color: "#FF6B35", + color: "var(--document-reader-accent)", marginBottom: 16, }, uploadTitle: { @@ -655,9 +661,9 @@ const styles: Record = { recropBtn: { flex: 1, padding: "12px 20px", - background: "rgba(255, 107, 53, 0.1)", - color: "#FF6B35", - border: "1px solid rgba(255, 107, 53, 0.3)", + background: "var(--document-reader-accent-soft)", + color: "var(--document-reader-accent)", + border: "1px solid var(--document-reader-accent-border)", borderRadius: 12, fontSize: 14, fontWeight: 600, @@ -679,8 +685,8 @@ const styles: Record = { extractBtn: { width: "100%", padding: "18px 32px", - background: "linear-gradient(135deg, #FF6B35 0%, #F7931E 100%)", - color: "var(--document-reader-text)", + background: "var(--document-reader-primary-bg)", + color: "var(--document-reader-accent-contrast)", border: "none", borderRadius: 16, fontSize: 16, @@ -693,7 +699,7 @@ const styles: Record = { padding: 32, borderRadius: 20, background: "var(--document-reader-card-bg)", - border: "1px solid rgba(255, 107, 53, 0.2)", + border: "1px solid var(--document-reader-accent-border-soft)", marginBottom: 24, position: "relative" as const, overflow: "hidden", @@ -703,7 +709,7 @@ const styles: Record = { top: 16, right: 16, fontSize: 10, - color: "rgba(255, 107, 53, 0.3)", + color: "var(--document-reader-accent-border)", fontWeight: 600, letterSpacing: "0.1em", textTransform: "uppercase" as const, @@ -711,13 +717,13 @@ const styles: Record = { cardHeader: { marginBottom: 32, paddingBottom: 20, - borderBottom: "1px solid rgba(255, 107, 53, 0.2)", + borderBottom: "1px solid var(--document-reader-accent-border-soft)", }, cardType: { fontSize: 13, fontWeight: 700, letterSpacing: "0.1em", - color: "#FF6B35", + color: "var(--document-reader-accent)", textTransform: "uppercase" as const, }, cardBody: { @@ -736,16 +742,16 @@ const styles: Record = { }, fieldValue: { fontSize: 18, - fontFamily: "'Instrument Serif', serif", + fontFamily: "var(--tz-font-display)", color: "var(--document-reader-text)", fontWeight: 400, }, downloadBtn: { width: "100%", padding: "16px 32px", - background: "rgba(255, 107, 53, 0.1)", - color: "#FF6B35", - border: "1px solid rgba(255, 107, 53, 0.3)", + background: "var(--document-reader-accent-soft)", + color: "var(--document-reader-accent)", + border: "1px solid var(--document-reader-accent-border)", borderRadius: 16, fontSize: 15, fontWeight: 600, @@ -789,7 +795,7 @@ const styles: Record = { }, cropperTitle: { fontSize: 24, - fontFamily: "'Instrument Serif', serif", + fontFamily: "var(--tz-font-display)", color: "var(--document-reader-text)", margin: "0 0 8px 0", textAlign: "center", @@ -800,8 +806,8 @@ const styles: Record = { alignItems: "center", paddingTop: 8, marginTop: 12, - borderTop: "1px dashed rgba(255, 107, 53, 0.25)", - fontFamily: "'DM Sans', sans-serif", + borderTop: "1px dashed var(--document-reader-accent-border-soft)", + fontFamily: "var(--tz-font-body)", position: "relative" as const, zIndex: 1, }, @@ -811,7 +817,7 @@ const styles: Record = { fontWeight: 700, letterSpacing: "0.12em", textTransform: "uppercase" as const, - color: "rgba(255, 107, 53, 0.6)", + color: "var(--document-reader-accent-muted)", }, timestampValue: { @@ -841,7 +847,7 @@ const styles: Record = { }, reactEasyCropMedia: {}, reactEasyCropArea: { - border: "2px solid #FF6B35", + border: "2px solid var(--document-reader-accent)", boxShadow: "0 0 0 9999px rgba(0, 0, 0, 0.5)", }, zoomControl: { @@ -865,7 +871,7 @@ const styles: Record = { justifyContent: "center", gap: 10, fontSize: 16, - color: "#b70d0dff", + color: "var(--document-reader-muted)", textAlign: "center", padding: "16px 20px", background: "var(--document-reader-surface-faint)", @@ -885,7 +891,7 @@ const styles: Record = { }, zoomValue: { fontSize: 14, - color: "#FF6B35", + color: "var(--document-reader-accent)", fontWeight: 600, minWidth: 45, textAlign: "right" as const, @@ -897,7 +903,8 @@ const styles: Record = { iconWrapper: { display: "inline-flex", padding: 16, - background: "rgba(255, 107, 53, 0.1)", + color: "var(--document-reader-accent)", + background: "var(--document-reader-accent-soft)", borderRadius: 20, marginBottom: 24, transition: "all 0.2s ease", @@ -919,8 +926,8 @@ const styles: Record = { cropBtn: { flex: 1, padding: "14px 24px", - background: "linear-gradient(135deg, #FF6B35 0%, #F7931E 100%)", - color: "var(--document-reader-text)", + background: "var(--document-reader-primary-bg)", + color: "var(--document-reader-accent-contrast)", border: "none", borderRadius: 12, fontSize: 15, @@ -934,11 +941,11 @@ const styles: Record = { margin: "0 auto 18px", padding: "12px 16px", borderRadius: "12px", - color: "#991b1b", - background: "rgba(254, 226, 226, 0.95)", - border: "1px solid rgba(220, 38, 38, 0.35)", + color: "var(--document-reader-danger-text)", + background: "var(--document-reader-danger-bg)", + border: "1px solid var(--document-reader-danger-border)", fontWeight: 600, }, }; -export default AadhaarCardReader; \ No newline at end of file +export default AadhaarCardReader; diff --git a/frontend/src/pages/AudioForm.tsx b/frontend/src/pages/AudioForm.tsx index 69132ff..a538c86 100644 --- a/frontend/src/pages/AudioForm.tsx +++ b/frontend/src/pages/AudioForm.tsx @@ -354,10 +354,13 @@ const UploadPage = () => {
    - +
    setName(e.target.value)} diff --git a/frontend/src/pages/Contact.tsx b/frontend/src/pages/Contact.tsx index b4f7d4f..9b102a5 100644 --- a/frontend/src/pages/Contact.tsx +++ b/frontend/src/pages/Contact.tsx @@ -1,73 +1,119 @@ -import { useState } from "react"; - -import "@/assets/styles/contact.css"; -import { FULL_API_URL } from '@/constants'; - -export const ContactUs = () => { - const [form, setForm] = useState({ name: "", email: "", message: "" }); - const [status, setStatus] = useState(""); - - const handleChange = (e) => - setForm({ ...form, [e.target.name]: e.target.value }); - - const handleSubmit = async (e) => { - e.preventDefault(); - setStatus("Sending..."); - - const res = await fetch(`${FULL_API_URL}/contact`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(form), - }); - - if (res.ok) { - setStatus("Message sent!"); - setForm({ name: "", email: "", message: "" }); - } else { - setStatus("Failed to send!"); - } - }; - - return ( -
    -

    Contact Us

    - - -
    - - - - -