Skip to content

Commit d803d5f

Browse files
committed
fix(chat): polish provider edit hover action
1 parent 87d17d1 commit d803d5f

3 files changed

Lines changed: 51 additions & 33 deletions

File tree

crates/agent-gateway/web/src/pages/chat/ChatHeader.tsx

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -304,43 +304,49 @@ export const ChatHeader = memo(function ChatHeader(props: {
304304
return (
305305
<div key={group.id} className="flex flex-col gap-0.5">
306306
{groupIndex > 0 ? <hr className="my-1 h-px border-0 bg-muted" /> : null}
307-
<div className="sticky top-0 z-10 flex h-[30px] shrink-0 items-stretch bg-popover/95 backdrop-blur supports-[backdrop-filter]:bg-popover/80">
307+
<div className="group sticky top-0 z-10 flex h-[30px] shrink-0 items-stretch bg-popover/95 backdrop-blur transition-colors hover:bg-muted/40 focus-within:bg-muted/40 supports-[backdrop-filter]:bg-popover/80">
308308
<button
309309
type="button"
310310
onClick={() => toggleGroup(group.id)}
311311
aria-expanded={expanded}
312-
title={
313-
expanded ? t("chat.collapseProvider") : t("chat.expandProvider")
314-
}
315-
className="model-selector-group-label flex min-w-0 flex-1 cursor-pointer items-center gap-1.5 px-2 py-0 text-left text-xs font-medium text-muted-foreground transition-colors hover:bg-muted/40 focus-visible:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 dark:text-white/80"
312+
className="model-selector-group-label flex min-w-0 flex-1 cursor-pointer items-center gap-1.5 px-2 py-0 text-left text-xs font-medium text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 dark:text-white/80"
316313
>
317314
<ProviderBrandIcon
318315
type={group.providerType}
319316
className="h-3.5 w-3.5 opacity-90"
320317
/>
321318
<span className="min-w-0 flex-1 truncate">{group.name}</span>
322-
<span className="inline-flex h-4 min-w-[1.1rem] shrink-0 items-center justify-center rounded-full bg-muted/70 px-1 text-[10px] tabular-nums">
323-
{group.opts.length}
324-
</span>
325-
<ChevronDown
326-
className={cn(
327-
"h-3.5 w-3.5 shrink-0 transition-transform duration-200",
328-
expanded && "rotate-180",
329-
)}
330-
/>
331319
</button>
332320
<button
333321
type="button"
334322
onClick={() => {
335323
setIsModelPickerOpen(false);
336324
onOpenSettings("providers", group.id);
337325
}}
338-
title={`${t("settings.editProvider")}: ${group.name}`}
339326
aria-label={`${t("settings.editProvider")}: ${group.name}`}
340-
className="flex w-7 shrink-0 cursor-pointer items-center justify-center text-muted-foreground/70 transition-colors hover:bg-muted/60 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30"
327+
className="pointer-events-none flex w-7 max-w-0 shrink-0 cursor-pointer items-center justify-center overflow-hidden text-muted-foreground/70 opacity-0 transition-[max-width,opacity,color,background-color] duration-150 group-hover:max-w-7 group-hover:pointer-events-auto group-hover:opacity-100 group-focus-within:max-w-7 group-focus-within:pointer-events-auto group-focus-within:opacity-100 hover:bg-muted/60 hover:text-foreground focus-visible:max-w-7 focus-visible:pointer-events-auto focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30"
341328
>
342329
<Pencil className="h-3.5 w-3.5" />
343330
</button>
331+
<button
332+
type="button"
333+
onClick={() => toggleGroup(group.id)}
334+
aria-expanded={expanded}
335+
aria-label={`${
336+
expanded ? t("chat.collapseProvider") : t("chat.expandProvider")
337+
}: ${group.name}`}
338+
className="model-selector-group-label flex shrink-0 cursor-pointer items-center gap-1.5 px-2 py-0 text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 dark:text-white/80"
339+
>
340+
<span className="inline-flex h-4 min-w-[1.1rem] shrink-0 items-center justify-center rounded-full bg-muted/70 px-1 text-[10px] tabular-nums">
341+
{group.opts.length}
342+
</span>
343+
<ChevronDown
344+
className={cn(
345+
"h-3.5 w-3.5 shrink-0 transition-transform duration-200",
346+
expanded && "rotate-180",
347+
)}
348+
/>
349+
</button>
344350
</div>
345351
{expanded
346352
? group.opts.map((option) => {

crates/agent-gui/src/pages/chat/components/ChatHeader.tsx

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -313,15 +313,12 @@ export const ChatHeader = memo(function ChatHeader(props: {
313313
{groupIndex > 0 ? (
314314
<hr className="my-1 h-px border-0 bg-border/30" />
315315
) : null}
316-
<div className="sticky top-0 z-10 flex h-[30px] shrink-0 items-stretch rounded-md bg-popover/60 backdrop-blur-xl supports-[backdrop-filter]:bg-popover/40">
316+
<div className="group sticky top-0 z-10 flex h-[30px] shrink-0 items-stretch rounded-md bg-popover/60 backdrop-blur-xl transition-colors hover:bg-muted/40 focus-within:bg-muted/40 supports-[backdrop-filter]:bg-popover/40">
317317
<button
318318
type="button"
319319
onClick={() => toggleGroup(group.id)}
320320
aria-expanded={expanded}
321-
title={
322-
expanded ? t("chat.collapseProvider") : t("chat.expandProvider")
323-
}
324-
className="model-selector-group-label flex min-w-0 flex-1 cursor-pointer items-center gap-1.5 rounded-l-md px-2 py-0 text-left text-xs font-medium uppercase tracking-wider text-muted-foreground/80 transition-colors hover:bg-muted/40 focus-visible:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 dark:text-white/80"
321+
className="model-selector-group-label flex min-w-0 flex-1 cursor-pointer items-center gap-1.5 rounded-l-md px-2 py-0 text-left text-xs font-medium uppercase tracking-wider text-muted-foreground/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 dark:text-white/80"
325322
>
326323
<ProviderBrandIcon
327324
type={group.providerType}
@@ -330,28 +327,37 @@ export const ChatHeader = memo(function ChatHeader(props: {
330327
<span className="min-w-0 flex-1 truncate normal-case tracking-normal">
331328
{group.name}
332329
</span>
333-
<span className="inline-flex h-4 min-w-[1.1rem] shrink-0 items-center justify-center rounded-full bg-muted/70 px-1 text-[calc(10px*var(--zone-font-scale,1))] tabular-nums tracking-normal">
334-
{group.opts.length}
335-
</span>
336-
<ChevronDown
337-
className={cn(
338-
"h-3.5 w-3.5 shrink-0 transition-transform duration-200",
339-
expanded && "rotate-180",
340-
)}
341-
/>
342330
</button>
343331
<button
344332
type="button"
345333
onClick={() => {
346334
setIsModelPickerOpen(false);
347335
onOpenSettings("providers", group.id);
348336
}}
349-
title={`${t("settings.editProvider")}: ${group.name}`}
350337
aria-label={`${t("settings.editProvider")}: ${group.name}`}
351-
className="flex w-7 shrink-0 cursor-pointer items-center justify-center rounded-r-md text-muted-foreground/70 transition-colors hover:bg-muted/60 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30"
338+
className="pointer-events-none flex w-7 max-w-0 shrink-0 cursor-pointer items-center justify-center overflow-hidden text-muted-foreground/70 opacity-0 transition-[max-width,opacity,color,background-color] duration-150 group-hover:max-w-7 group-hover:pointer-events-auto group-hover:opacity-100 group-focus-within:max-w-7 group-focus-within:pointer-events-auto group-focus-within:opacity-100 hover:bg-muted/60 hover:text-foreground focus-visible:max-w-7 focus-visible:pointer-events-auto focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30"
352339
>
353340
<Pencil className="h-3.5 w-3.5" />
354341
</button>
342+
<button
343+
type="button"
344+
onClick={() => toggleGroup(group.id)}
345+
aria-expanded={expanded}
346+
aria-label={`${
347+
expanded ? t("chat.collapseProvider") : t("chat.expandProvider")
348+
}: ${group.name}`}
349+
className="model-selector-group-label flex shrink-0 cursor-pointer items-center gap-1.5 rounded-r-md px-2 py-0 text-muted-foreground/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 dark:text-white/80"
350+
>
351+
<span className="inline-flex h-4 min-w-[1.1rem] shrink-0 items-center justify-center rounded-full bg-muted/70 px-1 text-[calc(10px*var(--zone-font-scale,1))] tabular-nums tracking-normal">
352+
{group.opts.length}
353+
</span>
354+
<ChevronDown
355+
className={cn(
356+
"h-3.5 w-3.5 shrink-0 transition-transform duration-200",
357+
expanded && "rotate-180",
358+
)}
359+
/>
360+
</button>
355361
</div>
356362
{expanded
357363
? group.opts.map((option) => {

crates/agent-gui/test/chat/execution-mode-model-picker.test.mjs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,16 @@ test("model pickers search models and providers", () => {
5454
}
5555
});
5656

57-
test("provider groups expose a direct edit affordance", () => {
57+
test("provider groups reveal the edit affordance before the count on hover", () => {
5858
for (const source of headerSources) {
5959
assert.match(source, /\bPencil\b/);
6060
assert.match(source, /t\("settings\.editProvider"\)/);
61+
assert.doesNotMatch(source, /title=\{`\$\{t\("settings\.editProvider"\)/);
62+
assert.doesNotMatch(source, /title=\{\s*expanded \? t\("chat\.collapseProvider"\)/);
63+
assert.match(source, /pointer-events-none flex w-7 max-w-0/);
64+
assert.match(source, /group-hover:max-w-7/);
65+
assert.match(source, /group-focus-within:max-w-7/);
66+
assert.ok(source.indexOf("<Pencil") < source.indexOf("{group.opts.length}"));
6167
assert.match(
6268
source,
6369
/setIsModelPickerOpen\(false\);\s+onOpenSettings\("providers", group\.id\);/,

0 commit comments

Comments
 (0)