Slider / range
Single value: drag the thumb, or tab to it and use the arrows
Stepped: snaps to ticks; the value bubble rides the thumb on hover / focus / drag
Range: two thumbs (min / max) that can't cross; each is its own labeled slider
Sampling: the real Local Engine knob ranges (Sampling.kt); value is mono, accent when set
Disabled: inert, still legible
Sizes: sm and md
Track + fill + thumb are all var(--token). The track is a recessed well, the fill is the plain cyan accent, the thumb is a lifted disc (neutral shadow + catch-light, never a glow). Only transform / shadow / brightness move; no fill tweens. The island is live.
// Web: React, consuming @dizyx/nockerl-tokens. (Web is the laggard platform;// this is the canonical API the published @dizyx/nockerl-react package exposes.)import { NockerlSlider, NockerlRangeSlider } from '@dizyx/nockerl-react';
export function SamplingControls() { const [temp, setTemp] = useState(1.0); const [band, setBand] = useState({ low: 20, high: 70 }); return ( <> {/* Real Local Engine sampling ranges (Sampling.kt) */} <NockerlSlider label="Temperature" value={temp} onChange={setTemp} min={0.1} max={2.0} step={0.05} /> <NockerlSlider label="Top-P (nucleus)" value={topP} onChange={setTopP} min={0} max={1} step={0.01} />
{/* Stepped with tick marks */} <NockerlSlider label="Render quality" value={q} onChange={setQ} min={0} max={6} step={1} ticks />
{/* Two-thumb range; thumbs can't cross */} <NockerlRangeSlider label="Price band" low={band.low} high={band.high} onChange={(low, high) => setBand({ low, high })} min={0} max={100} step={5} unit="$" /> </> );}// Android: Jetpack Compose (canonical). androidx.compose.material3.Slider.// Shipped in chat/ui/SamplingAdvancedSettings.kt for the per-session sampling knobs.// `steps` is the count of stops BETWEEN the endpoints: ((max - min) / step) - 1.Slider( value = temperature, // 1.0, the universal Qwen default onValueChange = { next -> temperature = snapToStep(next, spec) }, valueRange = 0.1f..2.0f, // Temperature (Sampling.kt) steps = computeSliderSteps(spec), // ((2.0 - 0.1) / 0.05) - 1 = 37 enabled = enabled,)
// Two-thumb range (Material RangeSlider), designed here; not yet shipped in-app.RangeSlider( value = low..high, onValueChange = { range -> low = range.start; high = range.endInclusive }, valueRange = 0f..100f, steps = 19, // ((100 - 0) / 5) - 1)// macOS: SwiftUI, from the NockerlDesign SwiftPM package. `NockerlSlider` WRAPS the// native `Slider` (macOS pointer feel, keyboard/VoiceOver come from the system) and// brands it: cyan `accentPrimary` tint + a persistent, REQUIRED Nockerl-typography// label (never placeholder-as-label, law §14). The label is the first argument.import NockerlDesign
// Continuous by default; pass `step:` for a stepped slider. Real Local Engine sampling// range (Sampling.kt): Temperature 0.1 to 2.0 step 0.05.NockerlSlider("Temperature", value: $temperature, in: 0.1...2.0, step: 0.05)
// Stepped slider with optional end captions (plain strings, not view builders).NockerlSlider( "Render quality", value: $quality, in: 0...6, step: 1, minimumLabel: "0", maximumLabel: "6")
// Disable via the standard SwiftUI path.NockerlSlider("Top-P (nucleus)", value: $topP, in: 0...1, step: 0.01) .disabled(!enabled)
// Two-thumb range: SwiftUI has no native range Slider, so NockerlDesign ships none// yet. It is a documented follow-up (compose two bound thumbs over one track).Parameters
Section titled “Parameters”| Prop | Type | Default | Description |
|---|---|---|---|
value * | number | ||
onChange * | (v: number) => void | ||
min | number | 0 | |
max | number | 100 | |
step | number | 1 | |
unit | string | '' | |
label * | string | ||
size | NockerlSliderSize | 'md' | |
disabled | boolean | false | |
ticks | boolean | false | Render tick marks + min/max tick labels under the lane (stepped slider). |
Also accepts the native <div> attributes (e.g. onClick, disabled, id, aria-*, data-*), forwarded straight through, plus a forwarded ref.
RangeSlider swaps value for low / high and takes onChange={(low, high) => …}; the two thumbs are bound so they can never cross.
| Parameter | Type | Default | Description |
|---|---|---|---|
value * | Float | Current value. RangeSlider takes a ClosedFloatingPointRange<Float> instead. | |
onValueChange * | (Float) -> Unit | Continuous callback. The app snaps via snapToStep before storing. | |
valueRange | ClosedFloatingPointRange<Float> | 0f..1f | Min/max, e.g. 0.1f..2.0f for Temperature (Sampling.kt). |
steps | Int | 0 | Stops BETWEEN the endpoints: ((max - min) / step) - 1. 0 = continuous. |
enabled | Boolean | true | When false, the track + thumb dim and ignore input. |
colors | SliderColors | SliderDefaults.colors() | Defaults to the theme primary (= brand cyan) for the active track + thumb. |
modifier | Modifier | Modifier | External modifier; pass Modifier.fillMaxWidth() for a full-width row. |
NockerlSlider wraps the native Slider over a Binding<Double> and snaps to
step. The label is required (persistent above the track + the a11y name). State
(isEnabled, focus) comes from the environment via .disabled(_:), not a parameter.
Unavailable on tvOS (SwiftUI ships no Slider there).
| Parameter | Type | Default | Description |
|---|---|---|---|
label * | String | First positional arg: the persistent label above the track and the a11y name. Empty is a runtime assert (placeholder-as-label banned, law §14). | |
value * | Binding<Double> | Two-way binding to the value. | |
in | ClosedRange<Double> | 0...1 | Bounds, e.g. 0.1...2.0 for Temperature. |
step | Double? | nil | Snap granularity. nil = continuous. |
minimumLabel | String? | nil | Optional caption at the track start (e.g. "0.1"). |
maximumLabel | String? | nil | Optional caption at the track end (e.g. "2.0"). |
.disabled(_:) | ViewModifier | Inert + dimmed; the standard SwiftUI disable path. |