From e867b7873e7fe3be65736773758e49e44a6d91ff Mon Sep 17 00:00:00 2001 From: josedario87 Date: Fri, 13 Feb 2026 21:41:56 -0600 Subject: [PATCH] feat: Add page_refresh global tool and update voice shortcut to Ctrl+Space - Add page_refresh tool to reload the page via MCP - Change push-to-talk shortcut from Ctrl+S to Ctrl+Space - Use capture phase for keyboard events to intercept before terminal --- frontend/src/components/FloatingVoice.vue | 34 +++++++++++-------- frontend/src/services/toolRegistry.ts | 2 +- .../services/tools/handlers/globalHandlers.ts | 15 ++++++++ .../src/services/tools/toolDefinitions.ts | 3 +- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/frontend/src/components/FloatingVoice.vue b/frontend/src/components/FloatingVoice.vue index a067cb2..10845c0 100644 --- a/frontend/src/components/FloatingVoice.vue +++ b/frontend/src/components/FloatingVoice.vue @@ -47,7 +47,7 @@ const displayText = computed(() => { if (interimTranscript.value) { return transcript.value + ' ' + interimTranscript.value } - return transcript.value || 'Presiona el micrófono o mantén Ctrl+S...' + return transcript.value || 'Presiona el micrófono o mantén Ctrl+Space...' }) const containerStyle = computed(() => { @@ -252,10 +252,11 @@ function stopDrag() { document.removeEventListener('mouseup', stopDrag) } -// Keyboard shortcut handlers (Ctrl+S) +// Keyboard shortcut handlers (Ctrl+Space) function handleKeyDown(e: KeyboardEvent) { - if (e.ctrlKey && e.key.toLowerCase() === 's') { + if (e.ctrlKey && e.key === ' ') { e.preventDefault() + e.stopImmediatePropagation() // Prevent terminal and other handlers from receiving // Ignore if already holding if (keyDownTime > 0) return @@ -267,19 +268,21 @@ function handleKeyDown(e: KeyboardEvent) { isOpen.value = true } - // Start recording after 500ms hold + // Start recording after 150ms hold holdTimeout = window.setTimeout(() => { if (keyDownTime > 0 && !isRecording.value) { isPushToTalk.value = true startRecording() } - }, 500) + }, 150) } } function handleKeyUp(e: KeyboardEvent) { - // Only react to 's' key release, ignore Control - if (e.key.toLowerCase() === 's' && keyDownTime > 0) { + // Only react to Space release when Ctrl+Space was pressed + if (e.key === ' ' && keyDownTime > 0) { + e.preventDefault() + e.stopImmediatePropagation() // Prevent terminal and other handlers from receiving console.log('[Voice] Key S released, isPushToTalk:', isPushToTalk.value, 'isRecording:', isRecording.value) if (holdTimeout) { @@ -287,9 +290,9 @@ function handleKeyUp(e: KeyboardEvent) { holdTimeout = null } - // If was push-to-talk recording, stop and send after 500ms + // If was push-to-talk recording, stop and send after 1200ms if (isPushToTalk.value && isRecording.value) { - console.log('[Voice] Stopping recording, will send in 500ms') + console.log('[Voice] Stopping recording, will send in 1200ms') stopRecording() setTimeout(() => { console.log('[Voice] Sending transcript:', transcript.value.trim()) @@ -301,7 +304,7 @@ function handleKeyUp(e: KeyboardEvent) { isPushToTalk.value = false close() } - }, 500) + }, 1200) } keyDownTime = 0 @@ -348,16 +351,17 @@ function sendTranscriptAndClose() { onMounted(() => { recognition = initRecognition() - document.addEventListener('keydown', handleKeyDown) - document.addEventListener('keyup', handleKeyUp) + // Use capture phase to intercept before terminal or other elements + document.addEventListener('keydown', handleKeyDown, { capture: true }) + document.addEventListener('keyup', handleKeyUp, { capture: true }) }) onBeforeUnmount(() => { stopRecording() recognition = null disconnectSocket() - document.removeEventListener('keydown', handleKeyDown) - document.removeEventListener('keyup', handleKeyUp) + document.removeEventListener('keydown', handleKeyDown, { capture: true }) + document.removeEventListener('keyup', handleKeyUp, { capture: true }) document.removeEventListener('mousemove', onDrag) document.removeEventListener('mouseup', stopDrag) if (holdTimeout) clearTimeout(holdTimeout) @@ -421,7 +425,7 @@ defineExpose({ {{ transcript }} {{ interimTranscript }} - Presiona el micrófono o mantén Ctrl+S... + Presiona el micrófono o mantén Ctrl+Space... diff --git a/frontend/src/services/toolRegistry.ts b/frontend/src/services/toolRegistry.ts index 423adca..3f59ef0 100644 --- a/frontend/src/services/toolRegistry.ts +++ b/frontend/src/services/toolRegistry.ts @@ -127,7 +127,7 @@ function getToolConfigs(): Map { // Category to tool names mapping const categoryTools: Record = { - global: ['get_current_page', 'navigate_to', 'list_available_tools', 'activate_tool', 'deactivate_tool', 'pin_tool'], + global: ['get_current_page', 'navigate_to', 'list_available_tools', 'activate_tool', 'deactivate_tool', 'pin_tool', 'page_refresh'], canvas: ['render_html', 'render_vue_component'], component: ['save_vue_component', 'load_vue_component', 'list_vue_components', 'delete_vue_component'], theme: ['get_design_tokens', 'get_active_theme', 'set_theme_variable', 'save_theme', 'list_themes', 'switch_theme', 'reset_theme'], diff --git a/frontend/src/services/tools/handlers/globalHandlers.ts b/frontend/src/services/tools/handlers/globalHandlers.ts index 2274c6c..ad3e17b 100644 --- a/frontend/src/services/tools/handlers/globalHandlers.ts +++ b/frontend/src/services/tools/handlers/globalHandlers.ts @@ -208,6 +208,21 @@ export function createGlobalHandlers(callbacks: ToolManagementCallbacks): ToolCo return `Herramienta "${args.tool_name}" pinneada. Permanecera activa al cambiar de pagina.` } + }, + { + name: 'page_refresh', + description: 'Recarga la pagina completamente.', + category: 'global', + schema: { + type: 'object', + properties: {} + }, + handler: () => { + setTimeout(() => { + window.location.reload() + }, 100) + return 'Recargando pagina...' + } } ] } diff --git a/frontend/src/services/tools/toolDefinitions.ts b/frontend/src/services/tools/toolDefinitions.ts index c575646..e97d3a3 100644 --- a/frontend/src/services/tools/toolDefinitions.ts +++ b/frontend/src/services/tools/toolDefinitions.ts @@ -15,6 +15,7 @@ export const ALL_TOOL_METAS: ToolMeta[] = [ { name: 'activate_tool', description: 'Activa una herramienta MCP', category: 'global' }, { name: 'deactivate_tool', description: 'Desactiva una herramienta MCP', category: 'global' }, { name: 'pin_tool', description: 'Pinnea una herramienta', category: 'global' }, + { name: 'page_refresh', description: 'Recarga la pagina completamente', category: 'global' }, // Canvas tools { name: 'render_html', description: 'Renderiza HTML en el canvas', category: 'canvas' }, @@ -65,7 +66,7 @@ export const ALL_TOOL_METAS: ToolMeta[] = [ { name: 'terminal_toggle', description: 'Alterna abrir/cerrar el terminal', category: 'terminal' }, { name: 'terminal_move', description: 'Mueve la ventana del terminal a una posicion', category: 'terminal' }, { name: 'terminal_resize', description: 'Cambia el tamano de la ventana del terminal', category: 'terminal' }, - + // Response UI tools { name: 'bubbleResponse', description: 'Muestra un mensaje del agente en la UI', category: 'terminal' } ]