Change auto_memory to be layer_offloading and allow you to set the amount to unload

This commit is contained in:
Jaret Burkett
2025-10-10 13:12:32 -06:00
parent 2c2fbf16ea
commit 1bc6dee127
11 changed files with 279 additions and 45 deletions

View File

@@ -296,3 +296,147 @@ export const FormGroup: React.FC<FormGroupProps> = props => {
</div>
);
};
export interface SliderInputProps extends InputProps {
value: number;
onChange: (value: number) => void;
min: number;
max: number;
step?: number;
disabled?: boolean;
showValue?: boolean;
}
export const SliderInput: React.FC<SliderInputProps> = props => {
const { label, value, onChange, min, max, step = 1, disabled, className, docKey = null, showValue = true } = props;
let { doc } = props;
if (!doc && docKey) {
doc = getDoc(docKey);
}
const trackRef = React.useRef<HTMLDivElement | null>(null);
const [dragging, setDragging] = React.useState(false);
const clamp = (v: number) => (v < min ? min : v > max ? max : v);
const snapToStep = (v: number) => {
if (!Number.isFinite(v)) return min;
const steps = Math.round((v - min) / step);
const snapped = min + steps * step;
return clamp(Number(snapped.toFixed(6)));
};
const percent = React.useMemo(() => {
if (max === min) return 0;
const p = ((value - min) / (max - min)) * 100;
return p < 0 ? 0 : p > 100 ? 100 : p;
}, [value, min, max]);
const calcFromClientX = React.useCallback(
(clientX: number) => {
const el = trackRef.current;
if (!el || !Number.isFinite(clientX)) return;
const rect = el.getBoundingClientRect();
const width = rect.right - rect.left;
if (!(width > 0)) return;
// Clamp ratio to [0, 1] so it can never flip ends.
const ratioRaw = (clientX - rect.left) / width;
const ratio = ratioRaw <= 0 ? 0 : ratioRaw >= 1 ? 1 : ratioRaw;
const raw = min + ratio * (max - min);
onChange(snapToStep(raw));
},
[min, max, step, onChange],
);
// Mouse/touch pointer drag
const onPointerDown = (e: React.PointerEvent) => {
if (disabled) return;
e.preventDefault();
// Capture the pointer so moves outside the element are still tracked correctly
try {
(e.currentTarget as HTMLElement).setPointerCapture?.(e.pointerId);
} catch {}
setDragging(true);
calcFromClientX(e.clientX);
const handleMove = (ev: PointerEvent) => {
ev.preventDefault();
calcFromClientX(ev.clientX);
};
const handleUp = (ev: PointerEvent) => {
setDragging(false);
// release capture if we got it
try {
(e.currentTarget as HTMLElement).releasePointerCapture?.(e.pointerId);
} catch {}
window.removeEventListener('pointermove', handleMove);
window.removeEventListener('pointerup', handleUp);
};
window.addEventListener('pointermove', handleMove);
window.addEventListener('pointerup', handleUp);
};
return (
<div className={classNames(className, disabled ? 'opacity-30 cursor-not-allowed' : '')}>
{label && (
<label className={labelClasses}>
{label}{' '}
{doc && (
<div className="inline-block ml-1 text-xs text-gray-500 cursor-pointer" onClick={() => openDoc(doc)}>
<CircleHelp className="inline-block w-4 h-4 cursor-pointer" />
</div>
)}
</label>
)}
<div className="flex items-center gap-3">
<div className="flex-1">
<div
ref={trackRef}
onPointerDown={onPointerDown}
className={classNames(
'relative w-full h-6 select-none outline-none',
disabled ? 'pointer-events-none' : 'cursor-pointer',
)}
>
{/* Thicker track */}
<div className="pointer-events-none absolute left-0 right-0 top-1/2 -translate-y-1/2 h-3 rounded-sm bg-gray-800 border border-gray-700" />
{/* Fill */}
<div
className="pointer-events-none absolute left-0 top-1/2 -translate-y-1/2 h-3 rounded-sm bg-blue-600"
style={{ width: `${percent}%` }}
/>
{/* Thumb */}
<div
onPointerDown={onPointerDown}
className={classNames(
'absolute top-1/2 -translate-y-1/2 -ml-2',
'h-4 w-4 rounded-full bg-white shadow border border-gray-300 cursor-pointer',
'after:content-[""] after:absolute after:inset-[-6px] after:rounded-full after:bg-transparent', // expands hit area
dragging ? 'ring-2 ring-blue-600' : '',
)}
style={{ left: `calc(${percent}% )` }}
/>
</div>
<div className="flex justify-between text-xs text-gray-500 mt-0.5 select-none">
<span>{min}</span>
<span>{max}</span>
</div>
</div>
{showValue && (
<div className="min-w-[3.5rem] text-right text-sm px-3 py-1 bg-gray-800 border border-gray-700 rounded-sm">
{Number.isFinite(value) ? value : ''}
</div>
)}
</div>
</div>
);
};