mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-08 20:30:59 +00:00
refactor(web): use shadcn reasoning slider
This commit is contained in:
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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 };
|
||||
Reference in New Issue
Block a user