refactor(web): use shadcn reasoning slider

This commit is contained in:
fdc310
2026-08-01 22:15:43 +08:00
parent f83a163b15
commit c48c345b13
5 changed files with 255 additions and 123 deletions
@@ -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;
}
@@ -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 (
<Popover>
@@ -83,40 +81,16 @@ export default function ReasoningLevelPicker({
<span>{currentLabel}</span>
<ChevronRight className="size-3.5" />
</div>
<div className={styles.control}>
<div className={styles.track} />
<div
className={styles.fill}
style={{
width: `calc(17px + (100% - 34px) * ${progress})`,
}}
/>
{safeLevels.map((level, index) => {
const tickProgress = index / denominator;
return (
<span
key={level}
className={`${styles.tick} ${index <= currentIndex ? styles.tickActive : ''}`}
style={{
left: `calc(17px + (100% - 34px) * ${tickProgress})`,
}}
/>
);
})}
<input
className={styles.input}
type="range"
min={0}
max={Math.max(0, safeLevels.length - 1)}
step={1}
value={currentIndex}
aria-label={t('models.reasoningLevel')}
aria-valuetext={currentLabel}
onChange={(event) =>
onChange(safeLevels[Number(event.target.value)])
}
/>
</div>
<Slider
className="mt-5"
min={0}
max={Math.max(0, safeLevels.length - 1)}
step={1}
value={[currentIndex]}
aria-label={t('models.reasoningLevel')}
aria-valuetext={currentLabel}
onValueChange={([index]) => onChange(safeLevels[index])}
/>
</PopoverContent>
</Popover>
);
+36
View File
@@ -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<typeof SliderPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
>(({ className, ...props }, ref) => (
<SliderPrimitive.Root
ref={ref}
data-slot="slider"
className={cn(
'relative flex w-full touch-none select-none items-center data-[disabled]:opacity-50',
className,
)}
{...props}
>
<SliderPrimitive.Track
data-slot="slider-track"
className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20"
>
<SliderPrimitive.Range
data-slot="slider-range"
className="absolute h-full bg-primary"
/>
</SliderPrimitive.Track>
<SliderPrimitive.Thumb
data-slot="slider-thumb"
className="block size-4 shrink-0 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
/>
</SliderPrimitive.Root>
));
Slider.displayName = SliderPrimitive.Root.displayName;
export { Slider };