From c48c345b13133ed6dee209956c8deb4cc8df27fb Mon Sep 17 00:00:00 2001 From: fdc310 <2213070223@qq.com> Date: Sat, 1 Aug 2026 22:15:43 +0800 Subject: [PATCH] refactor(web): use shadcn reasoning slider --- web/package.json | 1 + web/pnpm-lock.yaml | 207 ++++++++++++++++++ .../reasoning/ReasoningLevelPicker.module.css | 86 -------- .../reasoning/ReasoningLevelPicker.tsx | 48 +--- web/src/components/ui/slider.tsx | 36 +++ 5 files changed, 255 insertions(+), 123 deletions(-) delete mode 100644 web/src/app/home/components/reasoning/ReasoningLevelPicker.module.css create mode 100644 web/src/components/ui/slider.tsx diff --git a/web/package.json b/web/package.json index e896b975e..715bc546d 100644 --- a/web/package.json +++ b/web/package.json @@ -48,6 +48,7 @@ "@radix-ui/react-scroll-area": "^1.2.9", "@radix-ui/react-select": "^2.2.4", "@radix-ui/react-separator": "^1.1.8", + "@radix-ui/react-slider": "^1.4.7", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.2.4", "@radix-ui/react-tabs": "^1.1.11", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 10521306b..ce058c568 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -72,6 +72,9 @@ dependencies: '@radix-ui/react-separator': specifier: ^1.1.8 version: 1.1.8(@types/react-dom@19.2.3)(@types/react@19.2.10)(react-dom@19.2.1)(react@19.2.1) + '@radix-ui/react-slider': + specifier: ^1.4.7 + version: 1.4.7(@types/react-dom@19.2.3)(@types/react@19.2.10)(react-dom@19.2.1)(react@19.2.1) '@radix-ui/react-slot': specifier: ^1.2.3 version: 1.2.4(@types/react@19.2.10)(react@19.2.1) @@ -555,10 +558,18 @@ packages: resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} dev: false + /@radix-ui/number@1.1.3: + resolution: {integrity: sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==} + dev: false + /@radix-ui/primitive@1.1.3: resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} dev: false + /@radix-ui/primitive@1.1.7: + resolution: {integrity: sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==} + dev: false + /@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.3)(@types/react@19.2.10)(react-dom@19.2.1)(react@19.2.1): resolution: {integrity: sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==} peerDependencies: @@ -682,6 +693,29 @@ packages: react-dom: 19.2.1(react@19.2.1) dev: false + /@radix-ui/react-collection@1.1.15(@types/react-dom@19.2.3)(@types/react@19.2.10)(react-dom@19.2.1)(react@19.2.1): + resolution: {integrity: sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==} + 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 + dependencies: + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.10)(react@19.2.1) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.10)(react@19.2.1) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3)(@types/react@19.2.10)(react-dom@19.2.1)(react@19.2.1) + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.10)(react@19.2.1) + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + dev: false + /@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3)(@types/react@19.2.10)(react-dom@19.2.1)(react@19.2.1): resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} peerDependencies: @@ -718,6 +752,19 @@ packages: react: 19.2.1 dev: false + /@radix-ui/react-compose-refs@1.1.5(@types/react@19.2.10)(react@19.2.1): + resolution: {integrity: sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 19.2.10 + react: 19.2.1 + dev: false + /@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.3)(@types/react@19.2.10)(react-dom@19.2.1)(react@19.2.1): resolution: {integrity: sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww==} peerDependencies: @@ -769,6 +816,19 @@ packages: react: 19.2.1 dev: false + /@radix-ui/react-context@1.2.2(@types/react@19.2.10)(react@19.2.1): + resolution: {integrity: sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 19.2.10 + react: 19.2.1 + dev: false + /@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3)(@types/react@19.2.10)(react-dom@19.2.1)(react@19.2.1): resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} peerDependencies: @@ -815,6 +875,19 @@ packages: react: 19.2.1 dev: false + /@radix-ui/react-direction@1.1.4(@types/react@19.2.10)(react@19.2.1): + resolution: {integrity: sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 19.2.10 + react: 19.2.1 + dev: false + /@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3)(@types/react@19.2.10)(react-dom@19.2.1)(react@19.2.1): resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} peerDependencies: @@ -1104,6 +1177,26 @@ packages: react-dom: 19.2.1(react@19.2.1) dev: false + /@radix-ui/react-primitive@2.1.10(@types/react-dom@19.2.3)(@types/react@19.2.10)(react-dom@19.2.1)(react@19.2.1): + resolution: {integrity: sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==} + 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 + dependencies: + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.10)(react@19.2.1) + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + dev: false + /@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3)(@types/react@19.2.10)(react-dom@19.2.1)(react@19.2.1): resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} peerDependencies: @@ -1281,6 +1374,36 @@ packages: react-dom: 19.2.1(react@19.2.1) dev: false + /@radix-ui/react-slider@1.4.7(@types/react-dom@19.2.3)(@types/react@19.2.10)(react-dom@19.2.1)(react@19.2.1): + resolution: {integrity: sha512-mTSLf1GC/C0moWjTbvCM6Qn/gBjvlFt1azuWF2v7MN5C3Zq2U2J2lN3ZEYkpujuOU5Ro7A28wkviSxaKnG0BYg==} + 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 + dependencies: + '@radix-ui/number': 1.1.3 + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.3)(@types/react@19.2.10)(react-dom@19.2.1)(react@19.2.1) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.10)(react@19.2.1) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.10)(react@19.2.1) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.10)(react@19.2.1) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3)(@types/react@19.2.10)(react-dom@19.2.1)(react@19.2.1) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.10)(react@19.2.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.10)(react@19.2.1) + '@radix-ui/react-use-previous': 1.1.4(@types/react@19.2.10)(react@19.2.1) + '@radix-ui/react-use-size': 1.1.4(@types/react@19.2.10)(react@19.2.1) + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + dev: false + /@radix-ui/react-slot@1.2.3(@types/react@19.2.10)(react@19.2.1): resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} peerDependencies: @@ -1309,6 +1432,20 @@ packages: react: 19.2.1 dev: false + /@radix-ui/react-slot@1.3.3(@types/react@19.2.10)(react@19.2.1): + resolution: {integrity: sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.10)(react@19.2.1) + '@types/react': 19.2.10 + react: 19.2.1 + dev: false + /@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3)(@types/react@19.2.10)(react-dom@19.2.1)(react@19.2.1): resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==} peerDependencies: @@ -1469,6 +1606,22 @@ packages: react: 19.2.1 dev: false + /@radix-ui/react-use-controllable-state@1.2.6(@types/react@19.2.10)(react@19.2.1): + resolution: {integrity: sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.10)(react@19.2.1) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.10)(react@19.2.1) + '@types/react': 19.2.10 + react: 19.2.1 + dev: false + /@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.10)(react@19.2.1): resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} peerDependencies: @@ -1483,6 +1636,20 @@ packages: react: 19.2.1 dev: false + /@radix-ui/react-use-effect-event@0.0.5(@types/react@19.2.10)(react@19.2.1): + resolution: {integrity: sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.10)(react@19.2.1) + '@types/react': 19.2.10 + react: 19.2.1 + dev: false + /@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.10)(react@19.2.1): resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} peerDependencies: @@ -1524,6 +1691,19 @@ packages: react: 19.2.1 dev: false + /@radix-ui/react-use-layout-effect@1.1.4(@types/react@19.2.10)(react@19.2.1): + resolution: {integrity: sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 19.2.10 + react: 19.2.1 + dev: false + /@radix-ui/react-use-previous@1.1.1(@types/react@19.2.10)(react@19.2.1): resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} peerDependencies: @@ -1537,6 +1717,19 @@ packages: react: 19.2.1 dev: false + /@radix-ui/react-use-previous@1.1.4(@types/react@19.2.10)(react@19.2.1): + resolution: {integrity: sha512-XoSLhbRbqxFtgJoi2fNHA3C6pDlY34x508vUpUGoFZfvePfHXHbE1lC4FYFMnJWgiCRroSTw6fOsXQoVS9RwZg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@types/react': 19.2.10 + react: 19.2.1 + dev: false + /@radix-ui/react-use-rect@1.1.1(@types/react@19.2.10)(react@19.2.1): resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} peerDependencies: @@ -1565,6 +1758,20 @@ packages: react: 19.2.1 dev: false + /@radix-ui/react-use-size@1.1.4(@types/react@19.2.10)(react@19.2.1): + resolution: {integrity: sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.10)(react@19.2.1) + '@types/react': 19.2.10 + react: 19.2.1 + dev: false + /@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3)(@types/react@19.2.10)(react-dom@19.2.1)(react@19.2.1): resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} peerDependencies: diff --git a/web/src/app/home/components/reasoning/ReasoningLevelPicker.module.css b/web/src/app/home/components/reasoning/ReasoningLevelPicker.module.css deleted file mode 100644 index 3e55d5ea7..000000000 --- a/web/src/app/home/components/reasoning/ReasoningLevelPicker.module.css +++ /dev/null @@ -1,86 +0,0 @@ -.control { - position: relative; - height: 2.5rem; - margin-top: 0.75rem; -} - -.track, -.fill { - position: absolute; - top: 50%; - left: 0; - height: 2rem; - transform: translateY(-50%); - border-radius: 1rem; -} - -.track { - width: 100%; - background: var(--muted); -} - -.fill { - background: var(--primary); -} - -.tick { - position: absolute; - top: 50%; - width: 0.3rem; - height: 0.3rem; - transform: translate(-50%, -50%); - border-radius: 50%; - background: color-mix(in oklch, var(--muted-foreground) 46%, transparent); - pointer-events: none; -} - -.tickActive { - background: color-mix(in oklch, var(--primary-foreground) 42%, transparent); -} - -.input { - position: absolute; - inset: 0; - width: 100%; - height: 100%; - margin: 0; - appearance: none; - background: transparent; - cursor: pointer; -} - -.input::-webkit-slider-runnable-track { - height: 2rem; - background: transparent; -} - -.input::-webkit-slider-thumb { - width: 2.125rem; - height: 2.125rem; - margin-top: -0.0625rem; - appearance: none; - border: 1px solid var(--border); - border-radius: 50%; - background: var(--background); - box-shadow: 0 1px 4px color-mix(in oklch, var(--foreground) 20%, transparent); -} - -.input::-moz-range-track { - height: 2rem; - background: transparent; -} - -.input::-moz-range-thumb { - width: 2.125rem; - height: 2.125rem; - border: 1px solid var(--border); - border-radius: 50%; - background: var(--background); - box-shadow: 0 1px 4px color-mix(in oklch, var(--foreground) 20%, transparent); -} - -.input:focus-visible { - outline: 2px solid var(--ring); - outline-offset: 2px; - border-radius: 1rem; -} diff --git a/web/src/app/home/components/reasoning/ReasoningLevelPicker.tsx b/web/src/app/home/components/reasoning/ReasoningLevelPicker.tsx index 54c1439bd..81848911d 100644 --- a/web/src/app/home/components/reasoning/ReasoningLevelPicker.tsx +++ b/web/src/app/home/components/reasoning/ReasoningLevelPicker.tsx @@ -7,7 +7,7 @@ import { PopoverContent, PopoverTrigger, } from '@/components/ui/popover'; -import styles from './ReasoningLevelPicker.module.css'; +import { Slider } from '@/components/ui/slider'; export const REASONING_LEVELS: ReasoningLevel[] = [ 'provider_default', @@ -55,8 +55,6 @@ export default function ReasoningLevelPicker({ const currentLabel = t(REASONING_LEVEL_LABEL_KEYS[safeValue]); const isExplicit = safeValue !== 'provider_default'; const currentIndex = Math.max(0, safeLevels.indexOf(safeValue)); - const denominator = Math.max(1, safeLevels.length - 1); - const progress = currentIndex / denominator; return ( @@ -83,40 +81,16 @@ export default function ReasoningLevelPicker({ {currentLabel} - - - - {safeLevels.map((level, index) => { - const tickProgress = index / denominator; - return ( - - ); - })} - - onChange(safeLevels[Number(event.target.value)]) - } - /> - + onChange(safeLevels[index])} + /> ); diff --git a/web/src/components/ui/slider.tsx b/web/src/components/ui/slider.tsx new file mode 100644 index 000000000..4e3e69e1a --- /dev/null +++ b/web/src/components/ui/slider.tsx @@ -0,0 +1,36 @@ +import * as React from 'react'; +import * as SliderPrimitive from '@radix-ui/react-slider'; + +import { cn } from '@/lib/utils'; + +const Slider = React.forwardRef< + React.ComponentRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + + +)); +Slider.displayName = SliderPrimitive.Root.displayName; + +export { Slider };