feat: polish inbox and issue list workflows

This commit is contained in:
Dotta 2026-04-10 22:26:21 -05:00
parent 548721248e
commit dab95740be
37 changed files with 1674 additions and 411 deletions

View file

@ -10,12 +10,15 @@ import { useKeyboardShortcuts } from "./useKeyboardShortcuts";
function TestHarness({
onNewIssue,
onSearch,
}: {
onNewIssue: () => void;
onSearch?: () => void;
}) {
useKeyboardShortcuts({
enabled: true,
onNewIssue,
onSearch,
});
return <div>keyboard shortcuts test</div>;
@ -55,4 +58,52 @@ describe("useKeyboardShortcuts", () => {
root.unmount();
});
});
it("focuses the current page search target on slash", () => {
const root = createRoot(container);
const onSearch = vi.fn();
const input = document.createElement("input");
input.setAttribute("data-page-search-target", "true");
vi.spyOn(input, "getClientRects").mockReturnValue([{}] as unknown as DOMRectList);
document.body.appendChild(input);
act(() => {
root.render(<TestHarness onNewIssue={vi.fn()} onSearch={onSearch} />);
});
document.dispatchEvent(new KeyboardEvent("keydown", {
key: "/",
bubbles: true,
cancelable: true,
}));
expect(document.activeElement).toBe(input);
expect(onSearch).not.toHaveBeenCalled();
act(() => {
root.unmount();
});
input.remove();
});
it("falls back to quick search when the page has no search target", () => {
const root = createRoot(container);
const onSearch = vi.fn();
act(() => {
root.render(<TestHarness onNewIssue={vi.fn()} onSearch={onSearch} />);
});
document.dispatchEvent(new KeyboardEvent("keydown", {
key: "/",
bubbles: true,
cancelable: true,
}));
expect(onSearch).toHaveBeenCalledTimes(1);
act(() => {
root.unmount();
});
});
});

View file

@ -1,9 +1,14 @@
import { useEffect } from "react";
import { isKeyboardShortcutTextInputTarget } from "../lib/keyboardShortcuts";
import {
focusPageSearchShortcutTarget,
hasBlockingShortcutDialog,
isKeyboardShortcutTextInputTarget,
} from "../lib/keyboardShortcuts";
interface ShortcutHandlers {
enabled?: boolean;
onNewIssue?: () => void;
onSearch?: () => void;
onToggleSidebar?: () => void;
onTogglePanel?: () => void;
onShowShortcuts?: () => void;
@ -12,6 +17,7 @@ interface ShortcutHandlers {
export function useKeyboardShortcuts({
enabled = true,
onNewIssue,
onSearch,
onToggleSidebar,
onTogglePanel,
onShowShortcuts,
@ -29,6 +35,19 @@ export function useKeyboardShortcuts({
return;
}
// / → Page search when available, otherwise quick search
if (e.key === "/" && !e.metaKey && !e.ctrlKey && !e.altKey) {
if (hasBlockingShortcutDialog()) {
return;
}
e.preventDefault();
if (!focusPageSearchShortcutTarget()) {
onSearch?.();
}
return;
}
// ? → Show keyboard shortcuts cheatsheet
if (e.key === "?" && !e.metaKey && !e.ctrlKey && !e.altKey) {
e.preventDefault();
@ -57,5 +76,5 @@ export function useKeyboardShortcuts({
document.addEventListener("keydown", handleKeyDown);
return () => document.removeEventListener("keydown", handleKeyDown);
}, [enabled, onNewIssue, onToggleSidebar, onTogglePanel, onShowShortcuts]);
}, [enabled, onNewIssue, onSearch, onToggleSidebar, onTogglePanel, onShowShortcuts]);
}