feat: Add terminal UI control tools for MCP

- Add 5 terminal tools: open, close, toggle, move, resize
- Create terminalHandlers.ts with UI control functions
- Add terminal category to toolDefinitions and toolRegistry
- Expose FloatingTerminal controls via defineExpose
- Connect controls in App.vue via setTerminalControls
- Fix ToolsDropdown missing terminal category
This commit is contained in:
2026-02-13 18:42:47 -06:00
parent f3f0df9cf3
commit 3a734f2426
5 changed files with 71 additions and 18 deletions

View File

@@ -10,11 +10,13 @@ import FloatingTerminal from './components/FloatingTerminal.vue'
import PwaInstallBanner from './components/PwaInstallBanner.vue' import PwaInstallBanner from './components/PwaInstallBanner.vue'
import { initWebMCP, getWebMCP, startTokenPolling, stopTokenPolling, connectWithToken } from './services/webmcp' import { initWebMCP, getWebMCP, startTokenPolling, stopTokenPolling, connectWithToken } from './services/webmcp'
import { initToolRegistry, activatePageTools, initToolsOnRefresh } from './services/toolRegistry' import { initToolRegistry, activatePageTools, initToolsOnRefresh } from './services/toolRegistry'
import { setTerminalControls } from './services/tools/handlers/terminalHandlers'
import { useCanvasStore } from './stores/canvas' import { useCanvasStore } from './stores/canvas'
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const showTerminal = ref(false) const showTerminal = ref(false)
const terminalRef = ref<InstanceType<typeof FloatingTerminal> | null>(null)
const canvasStore = useCanvasStore() const canvasStore = useCanvasStore()
type PageName = 'home' | 'canvas' | 'components' | 'themes' | 'projects' | 'project-canvas' | 'database' | 'source' | 'terminal' | 'tools' type PageName = 'home' | 'canvas' | 'components' | 'themes' | 'projects' | 'project-canvas' | 'database' | 'source' | 'terminal' | 'tools'
@@ -30,6 +32,43 @@ onMounted(async () => {
const currentPage = (route.name as string) || 'canvas' const currentPage = (route.name as string) || 'canvas'
initToolsOnRefresh(currentPage as PageName) initToolsOnRefresh(currentPage as PageName)
// Setup terminal controls for MCP tools
setTerminalControls({
open: (x?: number, y?: number) => {
if (terminalRef.value) {
terminalRef.value.open(x, y)
} else {
showTerminal.value = true
}
},
close: () => {
if (terminalRef.value) {
terminalRef.value.close()
} else {
showTerminal.value = false
}
},
toggle: () => {
if (terminalRef.value) {
terminalRef.value.toggle()
} else {
showTerminal.value = !showTerminal.value
}
},
move: (x: number, y: number) => {
terminalRef.value?.move(x, y)
},
resize: (w: number, h: number) => {
terminalRef.value?.resize(w, h)
},
getState: () => {
if (terminalRef.value) {
return terminalRef.value.getState()
}
return { isOpen: showTerminal.value, position: { x: 0, y: 0 }, size: { w: 580, h: 360 } }
}
})
// Start polling for token if not connected // Start polling for token if not connected
const webmcp = getWebMCP() const webmcp = getWebMCP()
if (!webmcp?.isConnected) { if (!webmcp?.isConnected) {
@@ -94,7 +133,7 @@ watch(() => route.name, (newPage) => {
</button> </button>
<!-- Floating Terminal --> <!-- Floating Terminal -->
<FloatingTerminal v-model="showTerminal" /> <FloatingTerminal ref="terminalRef" v-model="showTerminal" />
</div> </div>
</template> </template>

View File

@@ -21,7 +21,8 @@ const categoryTools: Record<ToolCategory, string[]> = {
theme: ['get_design_tokens', 'get_active_theme', 'set_theme_variable', 'save_theme', 'list_themes', 'switch_theme', 'reset_theme'], theme: ['get_design_tokens', 'get_active_theme', 'set_theme_variable', 'save_theme', 'list_themes', 'switch_theme', 'reset_theme'],
database: ['list_tables', 'get_table_schema', 'get_table_data', 'get_database_stats', 'execute_query'], database: ['list_tables', 'get_table_schema', 'get_table_data', 'get_database_stats', 'execute_query'],
source: ['get_repo_info', 'list_repo_files', 'read_repo_file', 'search_repo_code'], source: ['get_repo_info', 'list_repo_files', 'read_repo_file', 'search_repo_code'],
project: ['list_canvases', 'create_canvas', 'get_canvas', 'update_canvas', 'delete_canvas', 'clone_canvas', 'add_component_to_canvas', 'remove_component_from_canvas', 'get_canvas_components'] project: ['list_canvases', 'create_canvas', 'get_canvas', 'update_canvas', 'delete_canvas', 'clone_canvas', 'add_component_to_canvas', 'remove_component_from_canvas', 'get_canvas_components'],
terminal: ['terminal_open', 'terminal_close', 'terminal_toggle', 'terminal_move', 'terminal_resize']
} }
const categories = computed(() => { const categories = computed(() => {

View File

@@ -19,6 +19,7 @@ import {
createDatabaseHandlers, createDatabaseHandlers,
createProjectCanvasHandlers, createProjectCanvasHandlers,
createSourceCodeHandlers, createSourceCodeHandlers,
createTerminalHandlers,
type ToolConfig type ToolConfig
} from './tools/handlers' } from './tools/handlers'
import { setRouter } from './tools/handlers/globalHandlers' import { setRouter } from './tools/handlers/globalHandlers'
@@ -111,7 +112,8 @@ function getToolConfigs(): Map<string, ToolConfig> {
...createThemeHandlers(), ...createThemeHandlers(),
...createDatabaseHandlers(), ...createDatabaseHandlers(),
...createProjectCanvasHandlers(), ...createProjectCanvasHandlers(),
...createSourceCodeHandlers() ...createSourceCodeHandlers(),
...createTerminalHandlers()
] ]
for (const config of allHandlers) { for (const config of allHandlers) {
@@ -129,21 +131,22 @@ const categoryTools: Record<ToolCategory, string[]> = {
theme: ['get_design_tokens', 'get_active_theme', 'set_theme_variable', 'save_theme', 'list_themes', 'switch_theme', 'reset_theme'], theme: ['get_design_tokens', 'get_active_theme', 'set_theme_variable', 'save_theme', 'list_themes', 'switch_theme', 'reset_theme'],
database: ['list_tables', 'get_table_schema', 'get_table_data', 'get_database_stats', 'execute_query'], database: ['list_tables', 'get_table_schema', 'get_table_data', 'get_database_stats', 'execute_query'],
source: ['get_repo_info', 'list_repo_files', 'read_repo_file', 'search_repo_code'], source: ['get_repo_info', 'list_repo_files', 'read_repo_file', 'search_repo_code'],
project: ['list_canvases', 'create_canvas', 'get_canvas', 'update_canvas', 'delete_canvas', 'clone_canvas', 'add_component_to_canvas', 'remove_component_from_canvas', 'get_canvas_components'] project: ['list_canvases', 'create_canvas', 'get_canvas', 'update_canvas', 'delete_canvas', 'clone_canvas', 'add_component_to_canvas', 'remove_component_from_canvas', 'get_canvas_components'],
terminal: ['terminal_open', 'terminal_close', 'terminal_toggle', 'terminal_move', 'terminal_resize']
} }
// Page to categories mapping // Page to categories mapping
const pageCategories: Record<PageName, ToolCategory[]> = { const pageCategories: Record<PageName, ToolCategory[]> = {
home: ['global', 'canvas', 'component', 'project'], home: ['global', 'canvas', 'component', 'project', 'terminal'],
canvas: ['global', 'canvas', 'component'], canvas: ['global', 'canvas', 'component', 'terminal'],
'project-canvas': ['global', 'canvas', 'component', 'project'], 'project-canvas': ['global', 'canvas', 'component', 'project', 'terminal'],
projects: ['global', 'project'], projects: ['global', 'project', 'terminal'],
components: ['global', 'component'], components: ['global', 'component', 'terminal'],
themes: ['global', 'theme'], themes: ['global', 'theme', 'terminal'],
database: ['global', 'database'], database: ['global', 'database', 'terminal'],
source: ['global', 'source'], source: ['global', 'source', 'terminal'],
terminal: ['global'], terminal: ['global', 'terminal'],
tools: ['global'] tools: ['global', 'terminal']
} }
let currentPage: PageName | null = null let currentPage: PageName | null = null

View File

@@ -10,13 +10,15 @@ export { createThemeHandlers } from './themeHandlers'
export { createDatabaseHandlers } from './databaseHandlers' export { createDatabaseHandlers } from './databaseHandlers'
export { createProjectCanvasHandlers } from './projectCanvasHandlers' export { createProjectCanvasHandlers } from './projectCanvasHandlers'
export { createSourceCodeHandlers } from './sourceCodeHandlers' export { createSourceCodeHandlers } from './sourceCodeHandlers'
export { createTerminalHandlers, setTerminalControls } from './terminalHandlers'
export type { TerminalControls } from './terminalHandlers'
export type ToolHandler = (args: any) => string | Promise<string> export type ToolHandler = (args: any) => string | Promise<string>
export interface ToolConfig { export interface ToolConfig {
name: string name: string
description: string description: string
category: 'global' | 'canvas' | 'component' | 'theme' | 'database' | 'source' | 'project' category: 'global' | 'canvas' | 'component' | 'theme' | 'database' | 'source' | 'project' | 'terminal'
schema: object schema: object
handler: ToolHandler handler: ToolHandler
} }

View File

@@ -1,4 +1,4 @@
export type ToolCategory = 'global' | 'canvas' | 'component' | 'theme' | 'database' | 'source' | 'project' export type ToolCategory = 'global' | 'canvas' | 'component' | 'theme' | 'database' | 'source' | 'project' | 'terminal'
export interface ToolMeta { export interface ToolMeta {
name: string name: string
@@ -57,7 +57,14 @@ export const ALL_TOOL_METAS: ToolMeta[] = [
{ name: 'clone_canvas', description: 'Clona un canvas existente', category: 'project' }, { name: 'clone_canvas', description: 'Clona un canvas existente', category: 'project' },
{ name: 'add_component_to_canvas', description: 'Agrega un componente a un canvas', category: 'project' }, { name: 'add_component_to_canvas', description: 'Agrega un componente a un canvas', category: 'project' },
{ name: 'remove_component_from_canvas', description: 'Remueve un componente de un canvas', category: 'project' }, { name: 'remove_component_from_canvas', description: 'Remueve un componente de un canvas', category: 'project' },
{ name: 'get_canvas_components', description: 'Obtiene los componentes de un canvas', category: 'project' } { name: 'get_canvas_components', description: 'Obtiene los componentes de un canvas', category: 'project' },
// Terminal UI tools
{ name: 'terminal_open', description: 'Abre la ventana flotante del terminal', category: 'terminal' },
{ name: 'terminal_close', description: 'Cierra la ventana flotante del terminal', category: 'terminal' },
{ 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' }
] ]
// Get all tool names // Get all tool names
@@ -83,5 +90,6 @@ export const CATEGORY_INFO: Record<ToolCategory, { label: string; color: string;
theme: { label: 'Theme', color: '#ec4899', icon: 'M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10' }, theme: { label: 'Theme', color: '#ec4899', icon: 'M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10' },
database: { label: 'Database', color: '#3b82f6', icon: 'M12 2C7 2 3 3.5 3 5v14c0 1.5 4 3 9 3s9-1.5 9-3V5c0-1.5-4-3-9-3z' }, database: { label: 'Database', color: '#3b82f6', icon: 'M12 2C7 2 3 3.5 3 5v14c0 1.5 4 3 9 3s9-1.5 9-3V5c0-1.5-4-3-9-3z' },
source: { label: 'Source', color: '#8b5cf6', icon: 'M16 18l6-6-6-6M8 6l-6 6 6 6' }, source: { label: 'Source', color: '#8b5cf6', icon: 'M16 18l6-6-6-6M8 6l-6 6 6 6' },
project: { label: 'Project', color: '#06b6d4', icon: 'M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z' } project: { label: 'Project', color: '#06b6d4', icon: 'M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z' },
terminal: { label: 'Terminal', color: '#22c55e', icon: 'M4 17l6-6-6-6M12 19h8' }
} }