Skip to content

Commit 0c37f67

Browse files
samejrclaude
andcommitted
feat(webapp): add a Columns shortcut and match filter popover metrics
- The reorder handle and the smart-column edit/remove buttons revealed themselves through the row's :focus-within, so toggling a checkbox kept them visible after the cursor left. They now key off hover, plus :focus-visible on the control itself, which programmatic and mouse focus don't match — so the keyboard path still shows them but a checkbox click doesn't. - Size the rows and the footer items to h-8 with px-2, matching the visible row height of the filter popovers' SelectItem (they were 1.8rem, visibly shorter). - Give the Columns button an "l" shortcut and a "Customize columns" tooltip, the same text-plus-ShortcutKey pairing the filter buttons use, and list it under Runs page in the keyboard shortcuts sheet. "l" is unused on all five lists this control appears on (it's the Logs page's level filter elsewhere). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent e12b622 commit 0c37f67

2 files changed

Lines changed: 52 additions & 12 deletions

File tree

apps/webapp/app/components/Shortcuts.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { ASK_AI_SHORTCUT, askAiCanOpen } from "~/components/dashboard-agent/ask-
66
import { useDashboardAgentAvailable } from "~/components/dashboard-agent/dashboardAgentOpenRequest";
77
import { NEW_CHAT_SHORTCUT } from "~/components/dashboard-agent/DashboardAgentHeader";
88
import { TOGGLE_PANEL_SHORTCUT } from "~/components/dashboard-agent/dashboardAgentLauncher";
9+
import { COLUMNS_SHORTCUT } from "~/components/runs/v3/RunsDisplayOptions";
910
import { useAskAiAvailability } from "~/hooks/useAskAiAvailability";
1011
import { useShortcutKeys } from "~/hooks/useShortcutKeys";
1112
import { Header3 } from "./primitives/Headers";
@@ -142,6 +143,9 @@ function ShortcutContent() {
142143
)}
143144
<div className="space-y-3">
144145
<Header3>Runs page</Header3>
146+
<Shortcut name="Customize columns">
147+
<ShortcutKey shortcut={COLUMNS_SHORTCUT} variant="medium/bright" />
148+
</Shortcut>
145149
<Shortcut name="Bulk action: Cancel runs">
146150
<ShortcutKey shortcut={{ key: "c" }} variant="medium/bright" />
147151
</Shortcut>

apps/webapp/app/components/runs/v3/RunsDisplayOptions.tsx

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
} from "@heroicons/react/20/solid";
99
import { StarIcon as StarIconOutline } from "@heroicons/react/24/outline";
1010
import { GripVerticalIcon } from "lucide-react";
11-
import { useMemo, useState } from "react";
11+
import { useMemo, useRef, useState } from "react";
1212
import { ColumnsIcon } from "~/assets/icons/ColumnsIcon";
1313
import { useFavoritePageToggle } from "~/components/navigation/favoritePages";
1414
import { Button } from "~/components/primitives/Buttons";
@@ -19,10 +19,13 @@ import {
1919
PopoverMenuItem,
2020
PopoverTrigger,
2121
} from "~/components/primitives/Popover";
22+
import { ShortcutKey } from "~/components/primitives/ShortcutKey";
23+
import { SimpleTooltip } from "~/components/primitives/Tooltip";
2224
import { useEnvironment } from "~/hooks/useEnvironment";
2325
import { useFeatures } from "~/hooks/useFeatures";
2426
import { useOptimisticLocation } from "~/hooks/useOptimisticLocation";
2527
import { useSearchParams } from "~/hooks/useSearchParam";
28+
import { useShortcutKeys } from "~/hooks/useShortcutKeys";
2629
import { cn } from "~/utils/cn";
2730
import {
2831
encodeColumnLayout,
@@ -41,6 +44,9 @@ function keyFor(col: ResolvedColumn): string {
4144

4245
type SmartEditTarget = { index: number; def: SmartColumnDef };
4346

47+
/** Opens the Columns popover. "l" is free on every list this control appears on. */
48+
export const COLUMNS_SHORTCUT = { key: "l" as const };
49+
4450
export function RunsDisplayOptions({
4551
sampleFilters,
4652
}: {
@@ -56,6 +62,16 @@ export function RunsDisplayOptions({
5662
const [editing, setEditing] = useState<SmartEditTarget | null>(null);
5763
const [dragKey, setDragKey] = useState<string | null>(null);
5864
const [overKey, setOverKey] = useState<string | null>(null);
65+
const triggerRef = useRef<HTMLButtonElement>(null);
66+
67+
useShortcutKeys({
68+
shortcut: COLUMNS_SHORTCUT,
69+
action: (event) => {
70+
event.preventDefault();
71+
event.stopPropagation();
72+
triggerRef.current?.click();
73+
},
74+
});
5975

6076
const runtime: RunColumnRuntime = {
6177
isManagedCloud,
@@ -141,11 +157,28 @@ export function RunsDisplayOptions({
141157
return (
142158
<>
143159
<Popover>
144-
<PopoverTrigger asChild>
145-
<Button variant="secondary/small" LeadingIcon={ColumnsIcon}>
146-
Columns
147-
</Button>
148-
</PopoverTrigger>
160+
<SimpleTooltip
161+
asChild
162+
side="bottom"
163+
disableHoverableContent
164+
button={
165+
// Plain wrapper: Button drops the pointer-event props Radix injects via asChild,
166+
// so the tooltip anchor can't be the Button itself (same as NotificationPanel).
167+
<div className="flex">
168+
<PopoverTrigger asChild>
169+
<Button ref={triggerRef} variant="secondary/small" LeadingIcon={ColumnsIcon}>
170+
Columns
171+
</Button>
172+
</PopoverTrigger>
173+
</div>
174+
}
175+
content={
176+
<span className="flex items-center gap-2">
177+
Customize columns
178+
<ShortcutKey shortcut={COLUMNS_SHORTCUT} variant="small" />
179+
</span>
180+
}
181+
/>
149182
<PopoverContent
150183
align="end"
151184
className="w-64 p-0"
@@ -194,6 +227,7 @@ export function RunsDisplayOptions({
194227
icon={PlusIcon}
195228
title="Add smart column…"
196229
onClick={() => setAddOpen(true)}
230+
className="h-8"
197231
/>
198232
{canFavorite && (
199233
<PopoverMenuItem
@@ -206,13 +240,15 @@ export function RunsDisplayOptions({
206240
}
207241
title={isFavorited ? "Remove from favorites" : "Save to favorites"}
208242
onClick={toggleFavorite}
243+
className="h-8"
209244
/>
210245
)}
211246
<PopoverMenuItem
212247
icon={ArrowUturnLeftIcon}
213248
title="Reset to default"
214249
onClick={reset}
215250
disabled={!layout.isCustomized}
251+
className="h-8"
216252
/>
217253
</div>
218254
</PopoverContent>
@@ -268,7 +304,7 @@ function ColumnRow({
268304
return (
269305
<div
270306
className={cn(
271-
"group relative flex h-[1.8rem] items-center rounded-sm transition-colors hover:bg-background-hover",
307+
"group relative flex h-8 items-center rounded-sm transition-colors hover:bg-background-hover",
272308
dragging && "opacity-40"
273309
)}
274310
draggable
@@ -289,7 +325,7 @@ function ColumnRow({
289325
{/* Native label so the whole name area toggles the column, matching CheckboxWithLabel. */}
290326
<label
291327
className={cn(
292-
"flex h-full min-w-0 flex-1 items-center gap-x-1.5 pl-[0.4rem]",
328+
"flex h-full min-w-0 flex-1 items-center gap-x-2 pl-2",
293329
locked ? "cursor-default" : "cursor-pointer"
294330
)}
295331
>
@@ -305,13 +341,13 @@ function ColumnRow({
305341
</span>
306342
{isSmart && <BoltIcon className="size-3.5 flex-none text-text-dimmed" />}
307343
</label>
308-
<div className="flex flex-none items-center gap-0.5 pr-[0.4rem]">
344+
<div className="flex flex-none items-center gap-0.5 pr-1">
309345
{onEdit && (
310346
<button
311347
type="button"
312348
onClick={onEdit}
313349
aria-label={`Edit ${col.def.label}`}
314-
className="flex size-6 cursor-pointer items-center justify-center rounded-sm text-text-dimmed opacity-0 transition hover:bg-charcoal-700 hover:text-text-bright focus-custom group-hover:opacity-100 group-focus-within:opacity-100"
350+
className="flex size-6 cursor-pointer items-center justify-center rounded-sm text-text-dimmed opacity-0 transition hover:bg-charcoal-700 hover:text-text-bright focus-custom focus-visible:opacity-100 group-hover:opacity-100"
315351
>
316352
<PencilSquareIcon className="size-4" />
317353
</button>
@@ -321,7 +357,7 @@ function ColumnRow({
321357
type="button"
322358
onClick={onRemove}
323359
aria-label={`Remove ${col.def.label}`}
324-
className="flex size-6 cursor-pointer items-center justify-center rounded-sm text-text-dimmed opacity-0 transition hover:bg-charcoal-700 hover:text-error focus-custom group-hover:opacity-100 group-focus-within:opacity-100"
360+
className="flex size-6 cursor-pointer items-center justify-center rounded-sm text-text-dimmed opacity-0 transition hover:bg-charcoal-700 hover:text-error focus-custom focus-visible:opacity-100 group-hover:opacity-100"
325361
>
326362
<XMarkIcon className="size-4" />
327363
</button>
@@ -338,7 +374,7 @@ function ColumnRow({
338374
onMove(1);
339375
}
340376
}}
341-
className="flex size-6 cursor-grab items-center justify-center rounded-sm text-text-dimmed opacity-0 transition hover:bg-charcoal-700 hover:text-text-bright focus-custom group-hover:opacity-100 group-focus-within:opacity-100 active:cursor-grabbing"
377+
className="flex size-6 cursor-grab items-center justify-center rounded-sm text-text-dimmed opacity-0 transition hover:bg-charcoal-700 hover:text-text-bright focus-custom focus-visible:opacity-100 group-hover:opacity-100 active:cursor-grabbing"
342378
>
343379
<GripVerticalIcon className="size-4" />
344380
</button>

0 commit comments

Comments
 (0)