Change input from a slider to a text input

This commit is contained in:
Matt Rubens 2025-02-09 23:06:45 -05:00
parent 2ccab1444b
commit be9a90489a
2 changed files with 51 additions and 31 deletions

View file

@ -9,21 +9,17 @@ interface TemperatureControlProps {
export const TemperatureControl = ({ value, onChange, maxValue = 1 }: TemperatureControlProps) => {
const [isCustomTemperature, setIsCustomTemperature] = useState(value !== undefined)
const [inputValue, setInputValue] = useState(value?.toString() ?? "0")
// Sync internal state with prop changes when switching profiles
useEffect(() => {
const hasCustomTemperature = value !== undefined
setIsCustomTemperature(hasCustomTemperature)
setInputValue(value?.toString() ?? "0")
}, [value])
return (
<div
style={{
marginTop: 10,
marginBottom: 15,
paddingLeft: 10,
borderLeft: "2px solid var(--vscode-button-background)",
}}>
<div>
<VSCodeCheckbox
checked={isCustomTemperature}
onChange={(e: any) => {
@ -39,31 +35,44 @@ export const TemperatureControl = ({ value, onChange, maxValue = 1 }: Temperatur
</VSCodeCheckbox>
<p style={{ fontSize: "12px", marginTop: "5px", color: "var(--vscode-descriptionForeground)" }}>
Controls randomness in the model's responses. Higher values make output more random, lower values make
it more deterministic.
Controls randomness in the model's responses.
</p>
{isCustomTemperature && (
<div>
<div
style={{
marginTop: 5,
marginBottom: 10,
paddingLeft: 10,
borderLeft: "2px solid var(--vscode-button-background)",
}}>
<div style={{ display: "flex", alignItems: "center", gap: "5px" }}>
<input aria-label="Temperature control range input"
type="range"
min="0"
max={maxValue}
step="0.05"
value={value ?? 0}
onChange={(e) => {
<input
aria-label="Temperature control text input"
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onBlur={(e) => {
const newValue = parseFloat(e.target.value)
onChange(isNaN(newValue) ? undefined : newValue)
if (!isNaN(newValue) && newValue >= 0 && newValue <= maxValue) {
onChange(newValue)
setInputValue(newValue.toString())
} else {
setInputValue(value?.toString() ?? "0") // Reset to last valid value
}
}}
style={{
flexGrow: 1,
accentColor: "var(--vscode-button-background)",
height: "2px",
width: "60px",
padding: "4px 8px",
border: "1px solid var(--vscode-input-border)",
background: "var(--vscode-input-background)",
color: "var(--vscode-input-foreground)",
}}
/>
<span style={{ minWidth: "45px", textAlign: "left" }}>{value?.toFixed(2)}</span>
</div>
<p style={{ fontSize: "12px", marginTop: "8px", color: "var(--vscode-descriptionForeground)" }}>
Higher values make output more random, lower values make it more deterministic.
</p>
</div>
)}
</div>

View file

@ -8,7 +8,7 @@ describe("TemperatureControl", () => {
const checkbox = screen.getByRole("checkbox")
expect(checkbox).not.toBeChecked()
expect(screen.queryByRole("slider")).not.toBeInTheDocument()
expect(screen.queryByRole("textbox")).not.toBeInTheDocument()
})
it("renders with custom temperature enabled", () => {
@ -18,9 +18,9 @@ describe("TemperatureControl", () => {
const checkbox = screen.getByRole("checkbox")
expect(checkbox).toBeChecked()
const slider = screen.getByRole("slider")
expect(slider).toBeInTheDocument()
expect(slider).toHaveValue("0.7")
const input = screen.getByRole("textbox")
expect(input).toBeInTheDocument()
expect(input).toHaveValue("0.7")
})
it("updates when checkbox is toggled", () => {
@ -38,12 +38,13 @@ describe("TemperatureControl", () => {
expect(onChange).toHaveBeenCalledWith(0.7)
})
it("updates temperature when slider changes", () => {
it("updates temperature when input loses focus", () => {
const onChange = jest.fn()
render(<TemperatureControl value={0.7} onChange={onChange} />)
const slider = screen.getByRole("slider")
fireEvent.change(slider, { target: { value: "0.8" } })
const input = screen.getByRole("textbox")
fireEvent.change(input, { target: { value: "0.8" } })
fireEvent.blur(input)
expect(onChange).toHaveBeenCalledWith(0.8)
})
@ -52,8 +53,18 @@ describe("TemperatureControl", () => {
const onChange = jest.fn()
render(<TemperatureControl value={1.5} onChange={onChange} maxValue={2} />)
const slider = screen.getByRole("slider")
expect(slider).toHaveAttribute("max", "2")
const input = screen.getByRole("textbox")
// Valid value within max
fireEvent.change(input, { target: { value: "1.8" } })
fireEvent.blur(input)
expect(onChange).toHaveBeenCalledWith(1.8)
// Invalid value above max
fireEvent.change(input, { target: { value: "2.5" } })
fireEvent.blur(input)
expect(input).toHaveValue("1.5") // Should revert to original value
expect(onChange).toHaveBeenCalledTimes(1) // Should not call onChange for invalid value
})
it("syncs checkbox state when value prop changes", () => {