Stats bar round 2 (owner): popover centered over the summary button
Round 1 centered the popover over the whole bar ROW — the owner meant the stats bar's own bottom-left content (the context · cost summary button). The popover now anchors to a relative wrapper around the button and centers over IT (left-1/2 -translate-x-1/2 of the wrapper), so it hangs balanced over the trigger, not offset toward the composer row's center. Outside-click + Esc closing unchanged from round 1.
This commit is contained in:
+17
-13
@@ -14,8 +14,8 @@ import { ModelControl } from "@/components/model-picker";
|
|||||||
* All folds come from core/prices.ts (sessionCost, contextWindow, turnCost)
|
* All folds come from core/prices.ts (sessionCost, contextWindow, turnCost)
|
||||||
* — no cost math lives here. Absent data renders "—", never zero (the
|
* — no cost math lives here. Absent data renders "—", never zero (the
|
||||||
* doctrine): no turns yet → every figure is "—"; turns the price table
|
* doctrine): no turns yet → every figure is "—"; turns the price table
|
||||||
* can't price count as unpriced, shown in the popover, never folded into a
|
* can't price count as unpriced, shown in the popover, never folded into
|
||||||
* fake $0. The RIGHT side is the model + thinking control (components/
|
* a fake $0. The RIGHT side is the model + thinking control (components/
|
||||||
* model-picker.tsx): one controller for the conversation agents, backed by
|
* model-picker.tsx): one controller for the conversation agents, backed by
|
||||||
* pi-ai's catalog; the buttons show the EFFECTIVE model (the record's
|
* pi-ai's catalog; the buttons show the EFFECTIVE model (the record's
|
||||||
* settings, else config's pin), while the left-side figures still fold the
|
* settings, else config's pin), while the left-side figures still fold the
|
||||||
@@ -61,14 +61,18 @@ export function StatsBar({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
ref={barRef}
|
||||||
data-testid="stats-bar"
|
data-testid="stats-bar"
|
||||||
// pointer-events-auto: the sticky composer wrapper is pointer-events-none
|
// pointer-events-auto: the sticky composer wrapper is pointer-events-none
|
||||||
// (so its transparent gradient area never blocks scrolling) — this row
|
// (so its transparent gradient area never blocks scrolling) — this row
|
||||||
// must re-enable events or every control on it is dead (the model-
|
// must re-enable events or every control on it is dead (the model-
|
||||||
// control wave caught this live: buttons rendered, clicks never landed).
|
// control wave caught this live: buttons rendered, clicks never landed).
|
||||||
ref={barRef}
|
|
||||||
className="pointer-events-auto relative flex items-center justify-between gap-3 px-3 pt-1.5 text-xs text-muted-foreground"
|
className="pointer-events-auto relative flex items-center justify-between gap-3 px-3 pt-1.5 text-xs text-muted-foreground"
|
||||||
>
|
>
|
||||||
|
{/* The summary button + its popover: the popover anchors to this
|
||||||
|
wrapper, CENTERED over the button itself (owner, stats round 2)
|
||||||
|
— not over the whole bar row, which made it line up oddly. */}
|
||||||
|
<div className="relative">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setOpen((o) => !o)}
|
onClick={() => setOpen((o) => !o)}
|
||||||
@@ -83,16 +87,6 @@ export function StatsBar({
|
|||||||
<span aria-hidden>·</span>
|
<span aria-hidden>·</span>
|
||||||
<span data-testid="stats-cost">{costLabel}</span>
|
<span data-testid="stats-cost">{costLabel}</span>
|
||||||
</button>
|
</button>
|
||||||
{/* The model + thinking control — one controller for the conversation
|
|
||||||
agents (prober/teacher); the record's `turns` above still folds
|
|
||||||
the ACTUAL models billed, regardless of what the picker says. */}
|
|
||||||
<ModelControl
|
|
||||||
chatId={chatId}
|
|
||||||
settings={settings}
|
|
||||||
onSaved={onSettingsSaved}
|
|
||||||
fallbackModelId={lastModel}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{open && (
|
{open && (
|
||||||
<div
|
<div
|
||||||
data-testid="stats-detail"
|
data-testid="stats-detail"
|
||||||
@@ -145,5 +139,15 @@ export function StatsBar({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{/* The model + thinking control — one controller for the conversation
|
||||||
|
agents (prober/teacher); the record's `turns` above still folds
|
||||||
|
the ACTUAL models billed, regardless of what the picker says. */}
|
||||||
|
<ModelControl
|
||||||
|
chatId={chatId}
|
||||||
|
settings={settings}
|
||||||
|
onSaved={onSettingsSaved}
|
||||||
|
fallbackModelId={lastModel}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user