Add number key toggle for output modal

When the output modal is open:
- Pressing the same number key closes the modal
- Pressing a different number key switches to that feature's output

Also adds test utilities for testing number key interactions and fixes
the setupMockProjectWithInProgressFeatures utility to properly set
__mockFeatures for the mock electron API.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Cody Seibert
2025-12-09 08:57:41 -05:00
parent 9a6e6ea594
commit 2d8f600209
4 changed files with 111 additions and 3 deletions

View File

@@ -32,8 +32,7 @@
"category": "Kanban",
"description": "When I edit a card, it's showing an input for the description refactor to also show a text area for description like we do on the add card, add feature card.",
"steps": [],
"status": "in_progress",
"startedAt": "2025-12-09T13:48:07.718Z"
"status": "in_progress"
},
{
"id": "feature-1765287613626-z01cksyg6",
@@ -55,5 +54,12 @@
"description": "Can you please add a shortcut for starting a new session?",
"steps": [],
"status": "verified"
},
{
"id": "feature-1765288315355-ihrm3jz83",
"category": "Kanban",
"description": "Can you please refactor it so if I were to press one of the number keys on my keyboard when the output modal is open, just go ahead and auto collapse it if it was the same number that I had open. If I were to press a different number on my number key, don't actually close it again.",
"steps": [],
"status": "verified"
}
]
]

View File

@@ -16,6 +16,8 @@ interface AgentOutputModalProps {
onClose: () => void;
featureDescription: string;
featureId: string;
/** Called when a number key (0-9) is pressed while the modal is open */
onNumberKeyPress?: (key: string) => void;
}
export function AgentOutputModal({
@@ -23,6 +25,7 @@ export function AgentOutputModal({
onClose,
featureDescription,
featureId,
onNumberKeyPress,
}: AgentOutputModalProps) {
const [output, setOutput] = useState<string>("");
const [isLoading, setIsLoading] = useState(true);
@@ -169,6 +172,29 @@ export function AgentOutputModal({
autoScrollRef.current = isAtBottom;
};
// Handle number key presses while modal is open
useEffect(() => {
if (!open || !onNumberKeyPress) return;
const handleKeyDown = (event: KeyboardEvent) => {
// Check if a number key (0-9) was pressed without modifiers
if (
!event.ctrlKey &&
!event.altKey &&
!event.metaKey &&
/^[0-9]$/.test(event.key)
) {
event.preventDefault();
onNumberKeyPress(event.key);
}
};
window.addEventListener("keydown", handleKeyDown);
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, [open, onNumberKeyPress]);
return (
<Dialog open={open} onOpenChange={onClose}>
<DialogContent

View File

@@ -698,6 +698,30 @@ export function BoardView() {
setShowOutputModal(true);
};
// Handle number key press when output modal is open
const handleOutputModalNumberKeyPress = useCallback((key: string) => {
// Convert key to index: 1-9 -> 0-8, 0 -> 9
const index = key === "0" ? 9 : parseInt(key, 10) - 1;
// Get the feature at that index from in-progress features
const targetFeature = inProgressFeaturesForShortcuts[index];
if (!targetFeature) {
// No feature at this index, do nothing
return;
}
// If pressing the same number key as the currently open feature, close the modal
if (targetFeature.id === outputFeature?.id) {
setShowOutputModal(false);
}
// If pressing a different number key, switch to that feature's output
else {
setOutputFeature(targetFeature);
// Modal stays open, just showing different content
}
}, [inProgressFeaturesForShortcuts, outputFeature?.id]);
const handleForceStopFeature = async (feature: Feature) => {
try {
await autoMode.stopFeature(feature.id);
@@ -1209,6 +1233,7 @@ export function BoardView() {
onClose={() => setShowOutputModal(false)}
featureDescription={outputFeature?.description || ""}
featureId={outputFeature?.id || ""}
onNumberKeyPress={handleOutputModalNumberKeyPress}
/>
{/* Delete All Verified Dialog */}

View File

@@ -894,6 +894,10 @@ export async function setupMockProjectWithInProgressFeatures(
};
localStorage.setItem("automaker-storage", JSON.stringify(mockState));
// Also store features in a global variable that the mock electron API can use
// This is needed because the board-view loads features from the file system
(window as any).__mockFeatures = mockFeatures;
},
options
);
@@ -1255,6 +1259,29 @@ export async function pressShortcut(page: Page, key: string): Promise<void> {
await page.keyboard.press(key);
}
/**
* Count the number of session items in the session list
*/
export async function countSessionItems(page: Page): Promise<number> {
const sessionList = page.locator('[data-testid="session-list"] [data-testid^="session-item-"]');
return await sessionList.count();
}
/**
* Wait for a new session to be created (by checking if a session item appears)
*/
export async function waitForNewSession(
page: Page,
options?: { timeout?: number }
): Promise<void> {
// Wait for any session item to appear
const sessionItem = page.locator('[data-testid^="session-item-"]').first();
await sessionItem.waitFor({
timeout: options?.timeout ?? 5000,
state: "visible",
});
}
/**
* Check if a shortcut key indicator is visible for a navigation item
*/
@@ -1650,3 +1677,27 @@ export async function setupMockProjectWithSkipTestsFeatures(
options
);
}
/**
* Press a number key (0-9) on the keyboard
*/
export async function pressNumberKey(page: Page, num: number): Promise<void> {
await page.keyboard.press(num.toString());
}
/**
* Get the modal title/description text to verify which feature's output is being shown
*/
export async function getAgentOutputModalDescription(page: Page): Promise<string | null> {
const modal = page.locator('[data-testid="agent-output-modal"]');
const description = modal.locator('[id="radix-\\:r.+\\:-description"]').first();
return await description.textContent().catch(() => null);
}
/**
* Check the dialog description content in the agent output modal
*/
export async function getOutputModalDescription(page: Page): Promise<string | null> {
const modalDescription = page.locator('[data-testid="agent-output-modal"] [data-slot="dialog-description"]');
return await modalDescription.textContent().catch(() => null);
}