Skip to content

Commit 35e216a

Browse files
authored
Merge pull request #879 from aj47/claude/fix-waylan-agent-key-Yiuzp
Claude/fix waylan agent key yiuzp
2 parents c93c953 + 0f770a0 commit 35e216a

File tree

6 files changed

+102
-25
lines changed

6 files changed

+102
-25
lines changed

apps/desktop/src/main/keyboard.ts

Lines changed: 85 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,29 @@ const keysPressed = new Map<string, number>()
212212
// allowing common modifier combos like Ctrl+C to cancel before recording begins).
213213
const HOLD_TO_RECORD_DELAY_MS = 250
214214

215+
// Helper to check if a key is a modifier key
216+
const isModifierKey = (key: string): boolean => {
217+
return (
218+
key === "ControlLeft" ||
219+
key === "ControlRight" ||
220+
key === "ShiftLeft" ||
221+
key === "ShiftRight" ||
222+
key === "Alt" ||
223+
key === "AltLeft" ||
224+
key === "AltRight" ||
225+
key === "MetaLeft" ||
226+
key === "MetaRight"
227+
)
228+
}
229+
215230
const hasRecentKeyPress = () => {
216231
if (keysPressed.size === 0) return false
217232

218233
const now = Date.now() / 1000
219-
return [...keysPressed.values()].some((time) => {
234+
return [...keysPressed.entries()].some(([key, time]) => {
235+
// Exclude modifier keys from the check - they should not block shortcuts
236+
// that use only modifier key combinations (like toggle-ctrl-alt)
237+
if (isModifierKey(key)) return false
220238
// 10 seconds
221239
// for some weird reasons sometime KeyRelease event is missing for some keys
222240
// so they stay in the map
@@ -308,12 +326,45 @@ export function listenToKeyboardEvents() {
308326
}, HOLD_TO_RECORD_DELAY_MS)
309327
}
310328

329+
const tryToggleMcpIfEligible = () => {
330+
const config = configStore.get()
331+
if (config.mcpToolsShortcut !== "toggle-ctrl-alt") {
332+
return
333+
}
334+
335+
// Both modifiers must be down
336+
if (!isPressedCtrlKey || !isPressedAltKey) return
337+
338+
// Guard against recent non-modifier presses
339+
if (hasRecentKeyPress()) return
340+
341+
// Cancel regular recording timer since MCP is prioritized
342+
cancelRecordingTimer()
343+
344+
if (isDebugKeybinds()) {
345+
logKeybinds("MCP tools triggered: Ctrl+Alt (toggle mode)")
346+
}
347+
348+
// Set state.isRecordingMcpMode BEFORE sending the message
349+
// This ensures the key release handlers know we're in MCP toggle mode
350+
// and won't prematurely close the panel when the user releases Ctrl/Alt
351+
if (!state.isRecording) {
352+
// Starting MCP recording - set the flag now so key release handlers know
353+
state.isRecordingMcpMode = true
354+
}
355+
// Note: When stopping, the recordEvent handler will set isRecordingMcpMode = false
356+
357+
// Toggle MCP recording on/off
358+
getWindowRendererHandlers("panel")?.startOrFinishMcpRecording.send()
359+
}
360+
311361

312362
const handleEvent = (e: RdevEvent) => {
313363
if (e.event_type === "KeyPress") {
314364
if (e.data.key === "ControlLeft" || e.data.key === "ControlRight") {
315365
isPressedCtrlKey = true
316366
tryStartMcpHoldIfEligible()
367+
tryToggleMcpIfEligible()
317368
if (isDebugKeybinds()) {
318369
logKeybinds("Ctrl key pressed, isPressedCtrlKey =", isPressedCtrlKey)
319370
}
@@ -333,6 +384,7 @@ export function listenToKeyboardEvents() {
333384
isPressedAltKey = true
334385
isPressedCtrlAltKey = isPressedCtrlKey && isPressedAltKey
335386
tryStartMcpHoldIfEligible()
387+
tryToggleMcpIfEligible()
336388
if (isDebugKeybinds()) {
337389
logKeybinds(
338390
"Alt key pressed, isPressedAltKey =",
@@ -1126,31 +1178,15 @@ export function listenToKeyboardEvents() {
11261178
cancelCustomMcpTimer()
11271179
}
11281180

1129-
// Skip built-in hold mode handling for toggle mode shortcuts
1130-
if (
1131-
(currentConfig.shortcut === "ctrl-slash") ||
1132-
(currentConfig.shortcut === "custom" && currentConfig.customShortcutMode === "toggle")
1133-
)
1134-
return
1135-
1136-
cancelRecordingTimer()
1181+
// Always handle MCP hold-ctrl-alt key releases, regardless of recording shortcut mode
1182+
// This must happen before the toggle mode early return below
11371183
cancelMcpRecordingTimer()
11381184

1139-
// Finish MCP hold on either modifier release
11401185
if (e.data.key === "ControlLeft" || e.data.key === "ControlRight") {
11411186
if (isHoldingCtrlAltKey) {
11421187
const panelHandlers = getWindowRendererHandlers("panel")
11431188
panelHandlers?.finishMcpRecording.send()
11441189
isHoldingCtrlAltKey = false
1145-
} else {
1146-
if (isHoldingCtrlKey) {
1147-
getWindowRendererHandlers("panel")?.finishRecording.send()
1148-
} else if (!state.isTextInputActive) {
1149-
// Only close panel if we're not in text input mode
1150-
stopRecordingAndHidePanelWindow()
1151-
}
1152-
1153-
isHoldingCtrlKey = false
11541190
}
11551191
}
11561192

@@ -1159,8 +1195,36 @@ export function listenToKeyboardEvents() {
11591195
const panelHandlers = getWindowRendererHandlers("panel")
11601196
panelHandlers?.finishMcpRecording.send()
11611197
isHoldingCtrlAltKey = false
1162-
} else if (!state.isTextInputActive) {
1163-
// Only close panel if we're not in text input mode
1198+
}
1199+
}
1200+
1201+
// Skip built-in hold mode handling for toggle mode shortcuts
1202+
// (only applies to regular recording, not MCP agent mode which is handled above)
1203+
if (
1204+
(currentConfig.shortcut === "ctrl-slash") ||
1205+
(currentConfig.shortcut === "custom" && currentConfig.customShortcutMode === "toggle")
1206+
)
1207+
return
1208+
1209+
cancelRecordingTimer()
1210+
1211+
// Finish regular hold-ctrl recording on Ctrl release
1212+
if (e.data.key === "ControlLeft" || e.data.key === "ControlRight") {
1213+
if (isHoldingCtrlKey) {
1214+
getWindowRendererHandlers("panel")?.finishRecording.send()
1215+
} else if (!state.isTextInputActive && !state.isRecordingMcpMode) {
1216+
// Only close panel if we're not in text input mode and not in MCP recording mode
1217+
// (MCP toggle mode should not close panel on key release)
1218+
stopRecordingAndHidePanelWindow()
1219+
}
1220+
isHoldingCtrlKey = false
1221+
}
1222+
1223+
// Close panel on Alt release if not in text input mode (and not in MCP mode, which is handled above)
1224+
if (e.data.key === "Alt" || e.data.key === "AltLeft" || e.data.key === "AltRight") {
1225+
if (!state.isTextInputActive && !state.isRecordingMcpMode) {
1226+
// Only close panel if we're not in text input mode and not in MCP recording mode
1227+
// (MCP toggle mode should not close panel on key release)
11641228
stopRecordingAndHidePanelWindow()
11651229
}
11661230
}

apps/desktop/src/main/tipc.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1758,12 +1758,17 @@ export const router = {
17581758
}),
17591759

17601760
recordEvent: t.procedure
1761-
.input<{ type: "start" | "end" }>()
1761+
.input<{ type: "start" | "end"; mcpMode?: boolean }>()
17621762
.action(async ({ input }) => {
17631763
if (input.type === "start") {
17641764
state.isRecording = true
1765+
// Track MCP mode state so main process knows if we're in MCP toggle mode
1766+
if (input.mcpMode !== undefined) {
1767+
state.isRecordingMcpMode = input.mcpMode
1768+
}
17651769
} else {
17661770
state.isRecording = false
1771+
state.isRecordingMcpMode = false
17671772
}
17681773
updateTrayIcon()
17691774
}),

apps/desktop/src/renderer/src/pages/onboarding.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,8 @@ function AgentStep({
516516
const getAgentShortcutDisplay = () => {
517517
if (mcpToolsShortcut === "hold-ctrl-alt") {
518518
return "Hold Ctrl+Alt"
519+
} else if (mcpToolsShortcut === "toggle-ctrl-alt") {
520+
return "Toggle Ctrl+Alt"
519521
} else if (mcpToolsShortcut === "ctrl-alt-slash") {
520522
return "Press Ctrl+Alt+/"
521523
} else if (mcpToolsShortcut === "custom" && config?.customMcpToolsShortcut) {
@@ -631,6 +633,7 @@ function AgentStep({
631633
</SelectTrigger>
632634
<SelectContent>
633635
<SelectItem value="hold-ctrl-alt">Hold Ctrl+Alt</SelectItem>
636+
<SelectItem value="toggle-ctrl-alt">Toggle Ctrl+Alt</SelectItem>
634637
<SelectItem value="ctrl-alt-slash">Ctrl+Alt+/</SelectItem>
635638
<SelectItem value="custom">Custom</SelectItem>
636639
</SelectContent>

apps/desktop/src/renderer/src/pages/panel.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ export function Component() {
144144
const shortcut = config.mcpToolsShortcut
145145
if (shortcut === "hold-ctrl-alt") {
146146
return "Release keys"
147+
} else if (shortcut === "toggle-ctrl-alt") {
148+
return "Ctrl+Alt"
147149
} else if (shortcut === "ctrl-alt-slash") {
148150
return "Ctrl+Alt+/"
149151
} else if (shortcut === "custom" && config.customMcpToolsShortcut) {
@@ -357,7 +359,9 @@ export function Component() {
357359
recorder.on("record-start", () => {
358360
setRecording(true)
359361
recordingRef.current = true
360-
tipcClient.recordEvent({ type: "start" })
362+
// Pass mcpMode to main process so it knows we're in MCP toggle mode
363+
// This is critical for preventing panel close on key release in toggle mode
364+
tipcClient.recordEvent({ type: "start", mcpMode: mcpModeRef.current })
361365
})
362366

363367
recorder.on("visualizer-data", (rms) => {

apps/desktop/src/renderer/src/pages/settings-general.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ export function Component() {
410410
<div className="space-y-2">
411411
<Select
412412
value={configQuery.data?.mcpToolsShortcut || "hold-ctrl-alt"}
413-
onValueChange={(value: "hold-ctrl-alt" | "ctrl-alt-slash" | "custom") => {
413+
onValueChange={(value: "hold-ctrl-alt" | "toggle-ctrl-alt" | "ctrl-alt-slash" | "custom") => {
414414
saveConfig({ mcpToolsShortcut: value })
415415
}}
416416
>
@@ -419,6 +419,7 @@ export function Component() {
419419
</SelectTrigger>
420420
<SelectContent>
421421
<SelectItem value="hold-ctrl-alt">Hold Ctrl+Alt</SelectItem>
422+
<SelectItem value="toggle-ctrl-alt">Toggle Ctrl+Alt</SelectItem>
422423
<SelectItem value="ctrl-alt-slash">Ctrl+Alt+/</SelectItem>
423424
<SelectItem value="custom">Custom</SelectItem>
424425
</SelectContent>

apps/desktop/src/shared/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ export type Config = {
406406
// MCP Tool Calling Configuration
407407
/** @deprecated MCP tools are now always enabled. This field is kept for backwards compatibility but ignored. */
408408
mcpToolsEnabled?: boolean
409-
mcpToolsShortcut?: "hold-ctrl-alt" | "ctrl-alt-slash" | "custom"
409+
mcpToolsShortcut?: "hold-ctrl-alt" | "toggle-ctrl-alt" | "ctrl-alt-slash" | "custom"
410410
customMcpToolsShortcut?: string
411411
customMcpToolsShortcutMode?: "hold" | "toggle" // Mode for custom MCP tools shortcut
412412
mcpToolsProviderId?: CHAT_PROVIDER_ID

0 commit comments

Comments
 (0)