Skip to content

Commit f31fed2

Browse files
committed
feat: Virtual key sheet navigation with the keyboard
1 parent dd253f3 commit f31fed2

3 files changed

Lines changed: 72 additions & 45 deletions

File tree

ui/app/workspace/logs/sheets/logDetailsSheet.tsx

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
import { SheetNavigationButtons } from "@/components/sheetNavigationButtons";
12
import { Button } from "@/components/ui/button";
23
import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet";
34
import { useGetLogByIdQuery } from "@/lib/store/apis/logsApi";
45
import { useGetPromptQuery } from "@/lib/store/apis/promptsApi";
56
import type { LogEntry } from "@/lib/types/logs";
6-
import { ChevronDown, ChevronUp, Loader2 } from "lucide-react";
7+
import { useSheetNavigation } from "@/hooks/useSheetNavigation";
8+
import { Loader2 } from "lucide-react";
79
import { useEffect, useState } from "react";
8-
import { useHotkeys } from "react-hotkeys-hook";
910
import { LogDetailView } from "./logDetailView";
1011

1112
interface LogDetailSheetProps {
@@ -61,13 +62,11 @@ export function LogDetailSheet({
6162
}, [shouldPoll]);
6263

6364
// Keyboard navigation: arrow up/down to navigate between logs
64-
useHotkeys("up", () => onNavigate?.("prev"), {
65-
enabled: open && hasPrev,
66-
preventDefault: true,
67-
});
68-
useHotkeys("down", () => onNavigate?.("next"), {
69-
enabled: open && hasNext,
70-
preventDefault: true,
65+
const { prev: prevKeys, next: nextKeys } = useSheetNavigation({
66+
enabled: open,
67+
hasPrev,
68+
hasNext,
69+
onNavigate: (direction) => onNavigate?.(direction),
7170
});
7271

7372
if (!log) return null;
@@ -109,30 +108,14 @@ export function LogDetailSheet({
109108
View Session
110109
</Button>
111110
) : null}
112-
<div className="flex items-center">
113-
<Button
114-
variant="ghost"
115-
className="size-8"
116-
disabled={!hasPrev}
117-
onClick={() => onNavigate?.("prev")}
118-
aria-label="Previous log"
119-
data-testid="logdetails-prev-button"
120-
type="button"
121-
>
122-
<ChevronUp className="size-4" />
123-
</Button>
124-
<Button
125-
variant="ghost"
126-
className="size-8"
127-
disabled={!hasNext}
128-
onClick={() => onNavigate?.("next")}
129-
aria-label="Next log"
130-
data-testid="logdetails-next-button"
131-
type="button"
132-
>
133-
<ChevronDown className="size-4" />
134-
</Button>
135-
</div>
111+
<SheetNavigationButtons
112+
hasPrev={hasPrev}
113+
hasNext={hasNext}
114+
onNavigate={(dir) => onNavigate?.(dir)}
115+
prevKeys={prevKeys}
116+
nextKeys={nextKeys}
117+
entityLabel="log"
118+
/>
136119
</>
137120
}
138121
/>

ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { SheetNavigationButtons } from "@/components/sheetNavigationButtons";
12
import { Badge } from "@/components/ui/badge";
23
import { Label } from "@/components/ui/label";
34
import { Progress } from "@/components/ui/progress";
@@ -17,11 +18,12 @@ import {
1718
TableHeader,
1819
TableRow,
1920
} from "@/components/ui/table";
21+
import { useSheetNavigation } from "@/hooks/useSheetNavigation";
22+
import { supportsCalendarAlignment } from "@/lib/constants/governance";
2023
import { ProviderIconType, RenderProviderIcon } from "@/lib/constants/icons";
2124
import { ProviderLabels, ProviderName } from "@/lib/constants/logs";
2225
import { VirtualKey } from "@/lib/types/governance";
2326
import { cn } from "@/lib/utils";
24-
import { supportsCalendarAlignment } from "@/lib/constants/governance";
2527
import {
2628
calculateUsagePercentage,
2729
formatCurrency,
@@ -83,11 +85,17 @@ function UsageLine({
8385
interface VirtualKeyDetailSheetProps {
8486
virtualKey: VirtualKey;
8587
onClose: () => void;
88+
onNavigate?: (direction: "prev" | "next") => void;
89+
hasPrev?: boolean;
90+
hasNext?: boolean;
8691
}
8792

8893
export default function VirtualKeyDetailSheet({
8994
virtualKey,
9095
onClose,
96+
onNavigate,
97+
hasPrev = false,
98+
hasNext = false,
9199
}: VirtualKeyDetailSheetProps) {
92100
const {
93101
assignedUsers,
@@ -98,6 +106,13 @@ export default function VirtualKeyDetailSheet({
98106
displayRateLimit,
99107
} = useVirtualKeyUsage(virtualKey);
100108

109+
const { prev: prevKeys, next: nextKeys } = useSheetNavigation({
110+
enabled: true,
111+
hasPrev,
112+
hasNext,
113+
onNavigate: (direction) => onNavigate?.(direction),
114+
});
115+
101116
const getEntityInfo = () => {
102117
if (virtualKey.team) {
103118
return { type: "Team", name: virtualKey.team.name };
@@ -117,21 +132,30 @@ export default function VirtualKeyDetailSheet({
117132
(displayRateLimit?.token_current_usage &&
118133
displayRateLimit?.token_max_limit &&
119134
displayRateLimit.token_current_usage >=
120-
displayRateLimit.token_max_limit) ||
135+
displayRateLimit.token_max_limit) ||
121136
(displayRateLimit?.request_current_usage &&
122137
displayRateLimit?.request_max_limit &&
123138
displayRateLimit.request_current_usage >=
124-
displayRateLimit.request_max_limit);
139+
displayRateLimit.request_max_limit);
125140

126141
return (
127142
<Sheet open onOpenChange={onClose}>
128143
<SheetContent className="flex w-full flex-col overflow-x-hidden p-8 sm:max-w-2xl">
129-
<SheetHeader className="flex flex-col items-start p-0">
130-
<SheetTitle>{virtualKey.name}</SheetTitle>
131-
<SheetDescription>
132-
{virtualKey.description ||
133-
"Virtual key details and usage information"}
134-
</SheetDescription>
144+
<SheetHeader className="flex flex-row items-center justify-between p-0">
145+
<div className="flex flex-col items-start">
146+
<SheetTitle>{virtualKey.name}</SheetTitle>
147+
<SheetDescription>
148+
{virtualKey.description || "Virtual key details and usage information"}
149+
</SheetDescription>
150+
</div>
151+
<SheetNavigationButtons
152+
hasPrev={hasPrev}
153+
hasNext={hasNext}
154+
onNavigate={(dir) => onNavigate?.(dir)}
155+
prevKeys={prevKeys}
156+
nextKeys={nextKeys}
157+
entityLabel="virtual key"
158+
/>
135159
</SheetHeader>
136160

137161
<div className="space-y-6">
@@ -223,7 +247,7 @@ export default function VirtualKeyDetailSheet({
223247

224248
<div className="space-y-3">
225249
{!virtualKey.provider_configs ||
226-
virtualKey.provider_configs.length === 0 ? (
250+
virtualKey.provider_configs.length === 0 ? (
227251
<span className="text-muted-foreground text-sm">
228252
No providers configured (deny-by-default)
229253
</span>
@@ -503,7 +527,7 @@ export default function VirtualKeyDetailSheet({
503527

504528
<div className="space-y-3">
505529
{!virtualKey.mcp_configs ||
506-
virtualKey.mcp_configs.length === 0 ? (
530+
virtualKey.mcp_configs.length === 0 ? (
507531
<span className="text-muted-foreground text-sm">
508532
No MCP clients configured (deny-by-default)
509533
</span>

ui/app/workspace/virtual-keys/views/virtualKeysTable.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,18 @@ export default function VirtualKeysTable({
431431
setSelectedVirtualKeyId(null);
432432
};
433433

434+
const selectedVirtualKeyIndex = useMemo(
435+
() => (selectedVirtualKeyId ? virtualKeys.findIndex((vk) => vk.id === selectedVirtualKeyId) : -1),
436+
[selectedVirtualKeyId, virtualKeys],
437+
);
438+
439+
const handleDetailNavigate = (direction: "prev" | "next") => {
440+
const newIndex = direction === "prev" ? selectedVirtualKeyIndex - 1 : selectedVirtualKeyIndex + 1;
441+
if (newIndex >= 0 && newIndex < virtualKeys.length) {
442+
setSelectedVirtualKeyId(virtualKeys[newIndex].id);
443+
}
444+
};
445+
434446
const toggleKeyVisibility = (vkId: string) => {
435447
const newRevealed = new Set(revealedKeys);
436448
if (newRevealed.has(vkId)) {
@@ -542,7 +554,15 @@ export default function VirtualKeysTable({
542554
/>
543555
)}
544556

545-
{showDetailSheet && selectedVirtualKey && <VirtualKeyDetailSheet virtualKey={selectedVirtualKey} onClose={handleDetailSheetClose} />}
557+
{showDetailSheet && selectedVirtualKey && (
558+
<VirtualKeyDetailSheet
559+
virtualKey={selectedVirtualKey}
560+
onClose={handleDetailSheetClose}
561+
onNavigate={handleDetailNavigate}
562+
hasPrev={selectedVirtualKeyIndex > 0}
563+
hasNext={selectedVirtualKeyIndex >= 0 && selectedVirtualKeyIndex < virtualKeys.length - 1}
564+
/>
565+
)}
546566

547567
{/* Export Dialog */}
548568
<Dialog open={showExportDialog} onOpenChange={setShowExportDialog}>

0 commit comments

Comments
 (0)