diff --git a/package-lock.json b/package-lock.json index 92b6f433..0de5f41f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,9 +19,17 @@ "@langchain/groq": "^0.1.2", "@langchain/openai": "^0.3.14", "@orama/orama": "^3.0.0-rc-2", + "@radix-ui/react-checkbox": "^1.1.3", "@radix-ui/react-collapsible": "^1.1.2", + "@radix-ui/react-dialog": "^1.1.3", "@radix-ui/react-dropdown-menu": "^2.1.2", + "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-popover": "^1.1.4", + "@radix-ui/react-scroll-area": "^1.2.2", + "@radix-ui/react-select": "^2.1.2", + "@radix-ui/react-slider": "^1.2.1", + "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-switch": "^1.1.1", "@radix-ui/react-tooltip": "^1.1.6", "@tabler/icons-react": "^2.14.0", "axios": "^1.3.4", @@ -4606,6 +4614,12 @@ "node": ">=18" } }, + "node_modules/@radix-ui/number": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz", + "integrity": "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==", + "license": "MIT" + }, "node_modules/@radix-ui/primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", @@ -4633,6 +4647,122 @@ } } }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.3.tgz", + "integrity": "sha512-HD7/ocp8f1B3e6OHygH0n7ZKjONkhciy1Nh0yuBgObqThc3oyx+vuMfFHKAknXRHHWVE9XvXStxJFyjUmB8PIw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", + "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-presence": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", + "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-primitive": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", + "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-slot": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", + "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collapsible": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.2.tgz", @@ -4816,6 +4946,204 @@ } } }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.3.tgz", + "integrity": "sha512-ujGvqQNkZ0J7caQyl8XuZRj2/TIrYcOGwqz5TeD1OMcCdfBuEMP0D12ve+8J5F9XuNUth3FAKFWo/wt0E/GJrQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.2", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-portal": "1.1.3", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-slot": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.1.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.6.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", + "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.2.tgz", + "integrity": "sha512-kEHnlhv7wUggvhuJPkyw4qspXLJOdYoAP4dO2c8ngGuXTq1w/HZp1YeVB+NQ2KbH1iEG+pvOCGYSqh9HZOz6hg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.1.tgz", + "integrity": "sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-portal": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.3.tgz", + "integrity": "sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-presence": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", + "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-primitive": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", + "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", + "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-direction": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", @@ -4939,6 +5267,29 @@ } } }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.0.tgz", + "integrity": "sha512-peLblDlFw/ngk3UWq0VnYaOLy6agTZZ+MUO/WhVfm14vJGML+xH4FAl2XQGLqdefjNb7ApRg6Yn7U42ZhmYXdw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-menu": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.2.tgz", @@ -5414,6 +5765,222 @@ } } }, + "node_modules/@radix-ui/react-scroll-area": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.2.tgz", + "integrity": "sha512-EFI1N/S3YxZEW/lJ/H1jY3njlvTd8tBmgKEn4GHi51+aMm94i6NmAJstsm5cu3yJwYqYc93gpCPm21FeAbFk6g==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", + "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-presence": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", + "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-primitive": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", + "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-slot": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", + "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.2.tgz", + "integrity": "sha512-rZJtWmorC7dFRi0owDmoijm6nSJH1tVw64QGiNIZ9PNLyBDtG+iAq+XGsya052At4BfarzY/Dhv9wrrUr6IMZA==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-collection": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.1", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.0", + "@radix-ui/react-portal": "1.1.2", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.6.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-visually-hidden": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.0.tgz", + "integrity": "sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.2.1.tgz", + "integrity": "sha512-bEzQoDW0XP+h/oGbutF5VMWJPAl/UU8IJjr7h02SOHDIIIxq+cep8nItVNoBV+OMmahCdqdF38FTpmXoqQUGvw==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-collection": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", @@ -5432,6 +5999,35 @@ } } }, + "node_modules/@radix-ui/react-switch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.1.1.tgz", + "integrity": "sha512-diPqDDoBcZPSicYoMWdWx+bCPuTRH4QSp9J+65IvtdS0Kuzt67bI6n32vCj8q6NZmYW/ah+2orOtMwcX5eQwIg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-tooltip": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.6.tgz", @@ -5720,6 +6316,21 @@ } } }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz", + "integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-rect": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", diff --git a/package.json b/package.json index 344ac7f4..62affbfb 100644 --- a/package.json +++ b/package.json @@ -80,8 +80,16 @@ "@langchain/groq": "^0.1.2", "@langchain/openai": "^0.3.14", "@orama/orama": "^3.0.0-rc-2", + "@radix-ui/react-checkbox": "^1.1.3", "@radix-ui/react-collapsible": "^1.1.2", + "@radix-ui/react-dialog": "^1.1.3", "@radix-ui/react-dropdown-menu": "^2.1.2", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-scroll-area": "^1.2.2", + "@radix-ui/react-select": "^2.1.2", + "@radix-ui/react-slider": "^1.2.1", + "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-switch": "^1.1.1", "@radix-ui/react-popover": "^1.1.4", "@radix-ui/react-tooltip": "^1.1.6", "@tabler/icons-react": "^2.14.0", diff --git a/src/LLMProviders/chatModelManager.ts b/src/LLMProviders/chatModelManager.ts index 728452aa..e82bad88 100644 --- a/src/LLMProviders/chatModelManager.ts +++ b/src/LLMProviders/chatModelManager.ts @@ -1,8 +1,8 @@ import { CustomModel, getModelKey, ModelConfig, setModelKey } from "@/aiParams"; import { BUILTIN_CHAT_MODELS, ChatModelProviders } from "@/constants"; import { getDecryptedKey } from "@/encryptionService"; -import { getSettings, subscribeToSettingsChange } from "@/settings/model"; -import { safeFetch } from "@/utils"; +import { getModelKeyFromModel, getSettings, subscribeToSettingsChange } from "@/settings/model"; +import { err2String, safeFetch } from "@/utils"; import { HarmBlockThreshold, HarmCategory } from "@google/generative-ai"; import { ChatAnthropic } from "@langchain/anthropic"; import { ChatCohere } from "@langchain/cohere"; @@ -78,8 +78,8 @@ export default class ChatModelManager { const isO1Model = modelName.startsWith("o1"); const baseConfig: ModelConfig = { modelName: modelName, - temperature: settings.temperature, - streaming: true, + temperature: customModel.temperature ?? settings.temperature, + streaming: customModel.stream ?? true, maxRetries: 3, maxConcurrency: 3, enableCors: customModel.enableCors, @@ -96,7 +96,7 @@ export default class ChatModelManager { fetch: customModel.enableCors ? safeFetch : undefined, }, // @ts-ignore - openAIOrgId: getDecryptedKey(settings.openAIOrgId), + openAIOrgId: getDecryptedKey(customModel.openAIOrgId || settings.openAIOrgId), ...this.handleOpenAIExtraArgs(isO1Model, settings.maxTokens, settings.temperature), }, [ChatModelProviders.ANTHROPIC]: { @@ -111,9 +111,11 @@ export default class ChatModelManager { }, [ChatModelProviders.AZURE_OPENAI]: { azureOpenAIApiKey: await getDecryptedKey(customModel.apiKey || settings.azureOpenAIApiKey), - azureOpenAIApiInstanceName: settings.azureOpenAIApiInstanceName, - azureOpenAIApiDeploymentName: settings.azureOpenAIApiDeploymentName, - azureOpenAIApiVersion: settings.azureOpenAIApiVersion, + azureOpenAIApiInstanceName: + customModel.azureOpenAIApiInstanceName || settings.azureOpenAIApiInstanceName, + azureOpenAIApiDeploymentName: + customModel.azureOpenAIApiDeploymentName || settings.azureOpenAIApiDeploymentName, + azureOpenAIApiVersion: customModel.azureOpenAIApiVersion || settings.azureOpenAIApiVersion, configuration: { baseURL: customModel.baseUrl, fetch: customModel.enableCors ? safeFetch : undefined, @@ -224,7 +226,7 @@ export default class ChatModelManager { const getDefaultApiKey = this.providerApiKeyMap[model.provider as ChatModelProviders]; const apiKey = model.apiKey || getDefaultApiKey(); - const modelKey = `${model.name}|${model.provider}`; + const modelKey = getModelKeyFromModel(model); modelMap[modelKey] = { hasApiKey: Boolean(model.apiKey || apiKey), AIConstructor: constructor, @@ -252,7 +254,7 @@ export default class ChatModelManager { } async setChatModel(model: CustomModel): Promise { - const modelKey = `${model.name}|${model.provider}`; + const modelKey = getModelKeyFromModel(model); if (!ChatModelManager.modelMap.hasOwnProperty(modelKey)) { throw new Error(`No model found for: ${modelKey}`); } @@ -268,7 +270,7 @@ export default class ChatModelManager { const modelConfig = await this.getModelConfig(model); - setModelKey(`${model.name}|${model.provider}`); + setModelKey(modelKey); try { const newModelInstance = new selectedModel.AIConstructor({ ...modelConfig, @@ -327,7 +329,7 @@ export default class ChatModelManager { // First try without CORS await tryPing(false); return true; - } catch { + } catch (firstError) { console.log("First ping attempt failed, trying with CORS..."); try { // Second try with CORS @@ -337,8 +339,12 @@ export default class ChatModelManager { ); return true; } catch (error) { - console.error("Chat model ping failed:", error); - throw error; + const msg = + "\nwithout CORS Error: " + + err2String(firstError) + + "\nwith CORS Error: " + + err2String(error); + throw new Error(msg); } } } diff --git a/src/LLMProviders/embeddingManager.ts b/src/LLMProviders/embeddingManager.ts index 9ca26ded..8f223885 100644 --- a/src/LLMProviders/embeddingManager.ts +++ b/src/LLMProviders/embeddingManager.ts @@ -3,8 +3,8 @@ import { CustomModel } from "@/aiParams"; import { BREVILABS_API_BASE_URL, EmbeddingModelProviders } from "@/constants"; import { getDecryptedKey } from "@/encryptionService"; import { CustomError } from "@/error"; -import { getSettings, subscribeToSettingsChange } from "@/settings/model"; -import { safeFetch } from "@/utils"; +import { err2String, safeFetch } from "@/utils"; +import { getSettings, subscribeToSettingsChange, getModelKeyFromModel } from "@/settings/model"; import { CohereEmbeddings } from "@langchain/cohere"; import { Embeddings } from "@langchain/core/embeddings"; import { GoogleGenerativeAIEmbeddings } from "@langchain/google-genai"; @@ -100,7 +100,7 @@ export default class EmbeddingManager { const apiKey = model.apiKey || this.providerApiKeyMap[model.provider as EmbeddingModelProviders](); - const modelKey = `${model.name}|${model.provider}`; + const modelKey = getModelKeyFromModel(model); modelMap[modelKey] = { hasApiKey: Boolean(apiKey), EmbeddingConstructor: constructor, @@ -126,7 +126,7 @@ export default class EmbeddingManager { // Get the custom model that matches the name and provider from the model key private getCustomModel(modelKey: string): CustomModel { return this.activeEmbeddingModels.filter((model) => { - const key = `${model.name}|${model.provider}`; + const key = getModelKeyFromModel(model); return modelKey === key; })[0]; } @@ -224,9 +224,12 @@ export default class EmbeddingManager { }, [EmbeddingModelProviders.AZURE_OPENAI]: { azureOpenAIApiKey: await getDecryptedKey(customModel.apiKey || settings.azureOpenAIApiKey), - azureOpenAIApiInstanceName: settings.azureOpenAIApiInstanceName, - azureOpenAIApiDeploymentName: settings.azureOpenAIApiEmbeddingDeploymentName, - azureOpenAIApiVersion: settings.azureOpenAIApiVersion, + azureOpenAIApiInstanceName: + customModel.azureOpenAIApiInstanceName || settings.azureOpenAIApiInstanceName, + azureOpenAIApiDeploymentName: + customModel.azureOpenAIApiEmbeddingDeploymentName || + settings.azureOpenAIApiEmbeddingDeploymentName, + azureOpenAIApiVersion: customModel.azureOpenAIApiVersion || settings.azureOpenAIApiVersion, configuration: { baseURL: customModel.baseUrl, fetch: customModel.enableCors ? safeFetch : undefined, @@ -274,7 +277,7 @@ export default class EmbeddingManager { // First try without CORS await tryPing(false); return true; - } catch { + } catch (firstError) { console.log("First ping attempt failed, trying with CORS..."); try { // Second try with CORS @@ -284,8 +287,12 @@ export default class EmbeddingManager { ); return true; } catch (error) { - console.error("Embedding model ping failed:", error); - throw error; + const msg = + "\nwithout CORS Error: " + + err2String(firstError) + + "\nwith CORS Error: " + + err2String(error); + throw new Error(msg); } } } diff --git a/src/aiParams.ts b/src/aiParams.ts index a87650bd..df86d6c0 100644 --- a/src/aiParams.ts +++ b/src/aiParams.ts @@ -73,6 +73,17 @@ export interface CustomModel { isBuiltIn?: boolean; enableCors?: boolean; core?: boolean; + stream?: boolean; + temperature?: number; + context?: number; + // OpenAI specific fields + openAIOrgId?: string; + + // Azure OpenAI specific fields + azureOpenAIApiInstanceName?: string; + azureOpenAIApiDeploymentName?: string; + azureOpenAIApiVersion?: string; + azureOpenAIApiEmbeddingDeploymentName?: string; } export function setModelKey(modelKey: string) { diff --git a/src/components/chat-components/ChatInput.tsx b/src/components/chat-components/ChatInput.tsx index aa031e0d..337c4f46 100644 --- a/src/components/chat-components/ChatInput.tsx +++ b/src/components/chat-components/ChatInput.tsx @@ -1,4 +1,4 @@ -import { CustomModel, useChainType, useModelKey } from "@/aiParams"; +import { useChainType, useModelKey } from "@/aiParams"; import { ChainType } from "@/chainFactory"; import { AddImageModal } from "@/components/modals/AddImageModal"; import { ListPromptModal } from "@/components/modals/ListPromptModal"; @@ -7,7 +7,7 @@ import { ContextProcessor } from "@/contextProcessor"; import { CustomPromptProcessor } from "@/customPromptProcessor"; import { COPILOT_TOOL_NAMES } from "@/LLMProviders/intentAnalyzer"; import { Mention } from "@/mentions/Mention"; -import { useSettingsValue } from "@/settings/model"; +import { getModelKeyFromModel, useSettingsValue } from "@/settings/model"; import { ChatMessage } from "@/sharedState"; import { getToolDescription } from "@/tools/toolManager"; import { extractNoteTitles } from "@/utils"; @@ -40,8 +40,6 @@ interface ChatInputProps { chatHistory: ChatMessage[]; } -const getModelKey = (model: CustomModel) => `${model.name}|${model.provider}`; - const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>( ( { @@ -447,8 +445,9 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
- {settings.activeModels.find((model) => getModelKey(model) === currentModelKey) - ?.name || "Select Model"} + {settings.activeModels.find( + (model) => getModelKeyFromModel(model) === currentModelKey + )?.name || "Select Model"} @@ -458,8 +457,8 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>( .filter((model) => model.enabled) .map((model) => ( setCurrentModelKey(getModelKey(model))} + key={getModelKeyFromModel(model)} + onSelect={() => setCurrentModelKey(getModelKeyFromModel(model))} > {model.name} diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx new file mode 100644 index 00000000..f2cd66c0 --- /dev/null +++ b/src/components/ui/button.tsx @@ -0,0 +1,50 @@ +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "@/lib/utils"; + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", + { + variants: { + variant: { + default: "bg-interactive-accent text-on-accent shadow hover:bg-interactive-accent/90", + destructive: "bg-modifier-error text-normal shadow-sm hover:bg-modifier-error/90", + outline: + "border border-border bg-primary shadow-sm hover:bg-interactive-accent hover:text-on-accent", + secondary: "bg-secondary text-normal shadow-sm hover:bg-secondary/80", + ghost: "hover:bg-interactive-accent hover:text-on-accent", + link: "text-accent underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2", + sm: "h-8 rounded-md px-3 text-xs", + lg: "h-10 rounded-md px-8", + icon: "h-9 w-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button"; + return ( + + ); + } +); +Button.displayName = "Button"; + +export { Button, buttonVariants }; diff --git a/src/components/ui/checkbox.tsx b/src/components/ui/checkbox.tsx new file mode 100644 index 00000000..21cf2862 --- /dev/null +++ b/src/components/ui/checkbox.tsx @@ -0,0 +1,28 @@ +import * as React from "react"; +import * as CheckboxPrimitive from "@radix-ui/react-checkbox"; +import { Check } from "lucide-react"; + +import { cn } from "@/lib/utils"; + +const Checkbox = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + +)); +Checkbox.displayName = CheckboxPrimitive.Root.displayName; + +export { Checkbox }; diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx new file mode 100644 index 00000000..2a2506ce --- /dev/null +++ b/src/components/ui/dialog.tsx @@ -0,0 +1,105 @@ +import * as React from "react"; +import * as DialogPrimitive from "@radix-ui/react-dialog"; +import { X } from "lucide-react"; + +import { cn } from "@/lib/utils"; + +const Dialog = DialogPrimitive.Root; + +const DialogTrigger = DialogPrimitive.Trigger; + +const DialogPortal = DialogPrimitive.Portal; + +const DialogClose = DialogPrimitive.Close; + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; + +const DialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + container?: HTMLElement | null; + } +>(({ className, children, container, ...props }, ref) => ( + + + + {children} + + + Close + + + +)); +DialogContent.displayName = DialogPrimitive.Content.displayName; + +const DialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( +
+); +DialogHeader.displayName = "DialogHeader"; + +const DialogFooter = ({ className, ...props }: React.HTMLAttributes) => ( +
+); +DialogFooter.displayName = "DialogFooter"; + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogTitle.displayName = DialogPrimitive.Title.displayName; + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogDescription.displayName = DialogPrimitive.Description.displayName; + +export { + Dialog, + DialogPortal, + DialogOverlay, + DialogTrigger, + DialogClose, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +}; diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx new file mode 100644 index 00000000..f2db3a99 --- /dev/null +++ b/src/components/ui/input.tsx @@ -0,0 +1,25 @@ +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +const Input = React.forwardRef>( + ({ className, type, ...props }, ref) => { + return ( + + ); + } +); +Input.displayName = "Input"; + +export { Input }; diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx new file mode 100644 index 00000000..dea58222 --- /dev/null +++ b/src/components/ui/label.tsx @@ -0,0 +1,19 @@ +import * as React from "react"; +import * as LabelPrimitive from "@radix-ui/react-label"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "@/lib/utils"; + +const labelVariants = cva( + "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" +); + +const Label = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & VariantProps +>(({ className, ...props }, ref) => ( + +)); +Label.displayName = LabelPrimitive.Root.displayName; + +export { Label }; diff --git a/src/components/ui/password-input.tsx b/src/components/ui/password-input.tsx new file mode 100644 index 00000000..ce51cd34 --- /dev/null +++ b/src/components/ui/password-input.tsx @@ -0,0 +1,86 @@ +import React, { useState, useEffect, useRef } from "react"; +import { Input } from "@/components/ui/input"; +import { cn } from "@/lib/utils"; +import { Eye, EyeOff } from "lucide-react"; +import { getDecryptedKey } from "@/encryptionService"; + +export function PasswordInput({ + value, + onChange, + placeholder, + disabled, + className, +}: { + value?: string | number; + onChange?: (value: string) => void; + placeholder?: string; + disabled?: boolean; + className?: string; +}) { + const [showPassword, setShowPassword] = useState(false); + const [displayValue, setDisplayValue] = useState(""); + const isFirstLoad = useRef(true); + + // Handle all value transformations in a single effect + useEffect(() => { + const processValue = async () => { + // Decrypt the value only on first load if it's an encrypted string + if (isFirstLoad.current && typeof value === "string" && value) { + try { + const decrypted = await getDecryptedKey(value); + setDisplayValue(decrypted); + } catch (error) { + console.error("Failed to decrypt value:", error); + setDisplayValue(value); + } + isFirstLoad.current = false; + } else { + // For subsequent updates or non-encrypted values, use the value directly + setDisplayValue(value || ""); + } + }; + + processValue(); + }, [value]); + + return ( +
+ onChange?.(e.target.value)} + placeholder={placeholder} + disabled={disabled} + className={cn("![padding-right:1.75rem] w-full")} + /> +
!disabled && setShowPassword(!showPassword)} + className={cn( + "absolute right-2 top-1/2 -translate-y-1/2", + "cursor-pointer", + disabled && "opacity-50 cursor-not-allowed" + )} + role="button" + aria-label={showPassword ? "Hide password" : "Show password"} + > + {showPassword ? ( + + ) : ( + + )} +
+
+ ); +} diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx index 470ef84f..a28d280a 100644 --- a/src/components/ui/popover.tsx +++ b/src/components/ui/popover.tsx @@ -11,15 +11,17 @@ const PopoverAnchor = PopoverPrimitive.Anchor; const PopoverContent = React.forwardRef< React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( - + React.ComponentPropsWithoutRef & { + container?: HTMLElement | null; + } +>(({ className, align = "center", sideOffset = 4, container, ...props }, ref) => ( + , + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + span]:line-clamp-1", + className + )} + {...props} + > + {children} + + + + +)); +SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; + +const SelectScrollUpButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName; + +const SelectScrollDownButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName; + +const SelectContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + container?: HTMLElement | null; + } +>(({ className, children, position = "popper", container, ...props }, ref) => ( + + + + + {children} + + + + +)); +SelectContent.displayName = SelectPrimitive.Content.displayName; + +const SelectLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SelectLabel.displayName = SelectPrimitive.Label.displayName; + +const SelectItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)); +SelectItem.displayName = SelectPrimitive.Item.displayName; + +const SelectSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SelectSeparator.displayName = SelectPrimitive.Separator.displayName; + +export { + Select, + SelectGroup, + SelectValue, + SelectTrigger, + SelectContent, + SelectLabel, + SelectItem, + SelectSeparator, + SelectScrollUpButton, + SelectScrollDownButton, +}; diff --git a/src/components/ui/setting-item.tsx b/src/components/ui/setting-item.tsx new file mode 100644 index 00000000..58f59c49 --- /dev/null +++ b/src/components/ui/setting-item.tsx @@ -0,0 +1,269 @@ +import React from "react"; +import { cn } from "@/lib/utils"; +import { Input } from "@/components/ui/input"; +import { useTab } from "@/contexts/TabContext"; +import { PasswordInput } from "@/components/ui/password-input"; +import { Textarea } from "@/components/ui/textarea"; +import { SettingSwitch } from "@/components/ui/setting-switch"; +import { ChevronDown } from "lucide-react"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { SettingSlider } from "@/components/ui/setting-slider"; + +// 定义输入控件的类型 +type InputType = + | "text" + | "password" + | "number" + | "textarea" + | "switch" + | "select" + | "custom" + | "slider" + | "dialog"; + +// Select选项的类型 +interface SelectOption { + label: string; + value: string | number; +} + +// 基础Props +interface BaseSettingItemProps { + type: InputType; + title: string; + description?: string | React.ReactNode; + className?: string; + disabled?: boolean; +} + +// 不同类型输入控件的Props +interface TextSettingItemProps extends BaseSettingItemProps { + type: "text" | "password" | "number"; + value?: string | number; + onChange?: (value: string) => void; + placeholder?: string; +} + +interface TextareaSettingItemProps extends BaseSettingItemProps { + type: "textarea"; + value?: string; + onChange?: (value: string) => void; + placeholder?: string; + rows?: number; +} + +interface SwitchSettingItemProps extends BaseSettingItemProps { + type: "switch"; + checked?: boolean; + onCheckedChange?: (checked: boolean) => void; +} + +interface SelectSettingItemProps extends BaseSettingItemProps { + type: "select"; + value?: string | number; + onChange?: (value: string) => void; + options: SelectOption[]; + placeholder?: string; +} + +interface CustomSettingItemProps extends BaseSettingItemProps { + type: "custom"; + children: React.ReactNode; +} + +// 添加 Slider 类型的 Props +interface SliderSettingItemProps extends BaseSettingItemProps { + type: "slider"; + value?: number; + onChange?: (value: number) => void; + min: number; + max: number; + step: number; +} + +// 添加 Dialog 类型的 Props +interface DialogSettingItemProps extends BaseSettingItemProps { + type: "dialog"; + dialogTitle?: string; + dialogDescription?: string; + trigger: React.ReactNode; + children: React.ReactNode; +} + +// 联合类型 +type SettingItemProps = + | TextSettingItemProps + | TextareaSettingItemProps + | SwitchSettingItemProps + | SelectSettingItemProps + | CustomSettingItemProps + | SliderSettingItemProps + | DialogSettingItemProps; + +export function SettingItem(props: SettingItemProps) { + const { title, description, className, disabled } = props; + const { modalContainer } = useTab(); + + const renderControl = () => { + switch (props.type) { + case "text": + case "number": + return ( + props.onChange?.(e.target.value)} + placeholder={props.placeholder} + disabled={disabled} + className="w-full sm:w-[200px]" + /> + ); + + case "password": + return ( + + ); + + case "textarea": + return ( +