feat(ui): add review keyboard shortcuts
This commit is contained in:
parent
116c0c982b
commit
d63fdd1def
@ -18,6 +18,15 @@ import { approveReviewItem, batchReviewItems, listAgentTimeline, listApprovalQue
|
||||
import { toast } from "@/lib/toast";
|
||||
import type { AgentTimelineItem, ApprovalQueueItem } from "@/lib/types";
|
||||
|
||||
function isKeyboardShortcutSafe(target: EventTarget | null) {
|
||||
if (!(target instanceof HTMLElement)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const tagName = target.tagName.toLowerCase();
|
||||
return tagName !== "input" && tagName !== "textarea" && tagName !== "select" && !target.isContentEditable;
|
||||
}
|
||||
|
||||
export default function ReviewsPage() {
|
||||
const [approvalQueue, setApprovalQueue] = useState<ApprovalQueueItem[]>([]);
|
||||
const [timeline, setTimeline] = useState<AgentTimelineItem[]>([]);
|
||||
@ -172,6 +181,88 @@ export default function ReviewsPage() {
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
function selectRelativeQueueItem(offset: number) {
|
||||
if (approvalQueue.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentIndex = Math.max(
|
||||
0,
|
||||
approvalQueue.findIndex((item) => item.id === featuredProposal?.id),
|
||||
);
|
||||
const nextIndex = Math.min(Math.max(currentIndex + offset, 0), approvalQueue.length - 1);
|
||||
setSelectedApprovalId(approvalQueue[nextIndex]?.id ?? null);
|
||||
}
|
||||
|
||||
function handleKeyDown(event: KeyboardEvent) {
|
||||
if (
|
||||
event.defaultPrevented ||
|
||||
event.metaKey ||
|
||||
event.ctrlKey ||
|
||||
event.altKey ||
|
||||
!isKeyboardShortcutSafe(event.target)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const key = event.key.toLowerCase();
|
||||
if (key === "arrowdown" || key === "j") {
|
||||
event.preventDefault();
|
||||
selectRelativeQueueItem(1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (key === "arrowup" || key === "k") {
|
||||
event.preventDefault();
|
||||
selectRelativeQueueItem(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (key === "x") {
|
||||
event.preventDefault();
|
||||
selectAllForBatch();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((key === "escape" || key === "c") && batchMode) {
|
||||
event.preventDefault();
|
||||
clearBatch();
|
||||
return;
|
||||
}
|
||||
|
||||
if (key === "a") {
|
||||
event.preventDefault();
|
||||
if (batchMode) {
|
||||
void handleBatchDecision("approved");
|
||||
} else {
|
||||
void handleDecision("approved");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (key === "r") {
|
||||
event.preventDefault();
|
||||
if (batchMode) {
|
||||
void handleBatchDecision("rejected");
|
||||
} else {
|
||||
void handleDecision("rejected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||
}, [
|
||||
approvalQueue,
|
||||
batchMode,
|
||||
clearBatch,
|
||||
featuredProposal?.id,
|
||||
handleBatchDecision,
|
||||
handleDecision,
|
||||
selectAllForBatch,
|
||||
]);
|
||||
|
||||
return (
|
||||
<AppShell
|
||||
title="Agent review"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user