Stats-control round 1 (owner): bare labels + intuitive dropdown close
Model + thinking control refinements (components/model-picker.tsx):
- Drop the '·' separator between the two buttons.
- The thinking button shows just the level ('High', 'Max') — same
doctrine as the model button showing just the model name; '—' when
unknown. Pinned in render-stats-bar (label + no-dot assertions).
- The dropdowns now close on any pointerdown outside the control
(the toggle buttons stay inside the container and keep their own
toggling) and on Esc — the intuitive dropdown contract, instead of
the button being the only way out.
Note: file is graph-stream-owned per SCOPE; touching it per the owner's
direct request (same as cd518a4) — flagged for master.
This commit is contained in:
@@ -101,6 +101,7 @@ export function ModelControl({
|
||||
const [query, setQuery] = useState("");
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const searchRef = useRef<HTMLInputElement>(null);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const defaults = catalog?.defaults;
|
||||
const effectiveId = settings?.model?.id ?? defaults?.model.id ?? fallbackModelId;
|
||||
@@ -114,6 +115,28 @@ export function ModelControl({
|
||||
if (open === "model") searchRef.current?.focus();
|
||||
}, [open]);
|
||||
|
||||
// The intuitive dropdown contract (owner, stats-control round 1): any
|
||||
// click outside the open menu — or an Esc keypress — closes it. Only the
|
||||
// menu's own surface keeps it open (the toggle buttons live inside the
|
||||
// same container and handle their own toggling in onClick).
|
||||
useEffect(() => {
|
||||
if (open === null) return;
|
||||
const onPointerDown = (e: PointerEvent) => {
|
||||
if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
|
||||
setOpen(null);
|
||||
}
|
||||
};
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === "Escape") setOpen(null);
|
||||
};
|
||||
document.addEventListener("pointerdown", onPointerDown);
|
||||
document.addEventListener("keydown", onKeyDown);
|
||||
return () => {
|
||||
document.removeEventListener("pointerdown", onPointerDown);
|
||||
document.removeEventListener("keydown", onKeyDown);
|
||||
};
|
||||
}, [open]);
|
||||
|
||||
const save = useCallback(
|
||||
async (next: ChatSettings) => {
|
||||
setError(null);
|
||||
@@ -153,7 +176,7 @@ export function ModelControl({
|
||||
.map((r) => r.m);
|
||||
|
||||
return (
|
||||
<div className="relative flex items-center gap-1.5" data-testid="model-control">
|
||||
<div ref={containerRef} className="relative flex items-center gap-1.5" data-testid="model-control">
|
||||
<button
|
||||
type="button"
|
||||
data-testid="stats-model"
|
||||
@@ -167,9 +190,8 @@ export function ModelControl({
|
||||
className="flex items-center gap-1.5 rounded-full border border-border/60 bg-card/70 px-2.5 py-1 transition-colors hover:border-border hover:bg-muted"
|
||||
>
|
||||
<span className="text-card-foreground">{effectiveId ? prettyModel(effectiveId) : "—"}</span>
|
||||
<span aria-hidden className="text-xxs text-muted-foreground">▾</span>
|
||||
<span aria-hidden className="text-xxs text-muted-foreground">▾</span>
|
||||
</button>
|
||||
<span aria-hidden>·</span>
|
||||
<button
|
||||
type="button"
|
||||
data-testid="stats-thinking"
|
||||
@@ -181,8 +203,10 @@ export function ModelControl({
|
||||
title="Thinking level — applies next turn"
|
||||
className="flex items-center gap-1.5 rounded-full border border-border/60 bg-card/70 px-2.5 py-1 transition-colors hover:border-border hover:bg-muted"
|
||||
>
|
||||
{/* Just the level — the button reads like the model button reads
|
||||
the model name (owner, stats-control round 1). */}
|
||||
<span className="text-card-foreground">
|
||||
{effectiveLevel === undefined ? "Thinking —" : `Thinking: ${LEVEL_LABEL[effectiveLevel] ?? effectiveLevel}`}
|
||||
{effectiveLevel === undefined ? "—" : (LEVEL_LABEL[effectiveLevel] ?? effectiveLevel)}
|
||||
</span>
|
||||
<span aria-hidden className="text-xxs text-muted-foreground">▾</span>
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user