UNPKG

interm-mcp

Version:

MCP server for terminal applications and TUI automation with 127 tools

242 lines (241 loc) 9.89 kB
import { MouseManager } from './mouse-manager.js'; import { TerminalManager } from './terminal-manager.js'; import { createTerminalError, handleError } from './utils/error-utils.js'; export class AdvancedMouseManager { static instance; mouseManager; terminalManager; accelerationSettings = new Map(); pressureSettings = new Map(); focusSettings = new Map(); eventFilters = new Map(); multiClickCounters = new Map(); constructor() { this.mouseManager = MouseManager.getInstance(); this.terminalManager = TerminalManager.getInstance(); } static getInstance() { if (!AdvancedMouseManager.instance) { AdvancedMouseManager.instance = new AdvancedMouseManager(); } return AdvancedMouseManager.instance; } async configureAcceleration(sessionId, settings) { try { const session = this.terminalManager.getSession(sessionId); if (!session) { throw createTerminalError('SESSION_NOT_FOUND', `Session ${sessionId} not found`); } const currentSettings = this.accelerationSettings.get(sessionId) || { enabled: false, sensitivity: 1.0, threshold: 5, maxSpeed: 3.0 }; const newSettings = { ...currentSettings, ...settings }; this.accelerationSettings.set(sessionId, newSettings); } catch (error) { throw handleError(error, `Failed to configure mouse acceleration for session ${sessionId}`); } } async configurePressureSensitivity(sessionId, settings) { try { const session = this.terminalManager.getSession(sessionId); if (!session) { throw createTerminalError('SESSION_NOT_FOUND', `Session ${sessionId} not found`); } const currentSettings = this.pressureSettings.get(sessionId) || { enabled: false, threshold: 0.5, maxPressure: 1.0 }; const newSettings = { ...currentSettings, ...settings }; this.pressureSettings.set(sessionId, newSettings); } catch (error) { throw handleError(error, `Failed to configure pressure sensitivity for session ${sessionId}`); } } async configureFocusFollowMouse(sessionId, settings) { try { const session = this.terminalManager.getSession(sessionId); if (!session) { throw createTerminalError('SESSION_NOT_FOUND', `Session ${sessionId} not found`); } const currentSettings = this.focusSettings.get(sessionId) || { enabled: false, delay: 100, zones: [] }; const newSettings = { ...currentSettings, ...settings }; this.focusSettings.set(sessionId, newSettings); // Set up focus zones if enabled if (newSettings.enabled && newSettings.zones.length > 0) { this.setupFocusZones(sessionId, newSettings); } } catch (error) { throw handleError(error, `Failed to configure focus-follow-mouse for session ${sessionId}`); } } setupFocusZones(sessionId, settings) { // This would integrate with a mouse tracking system // For now, we simulate the setup console.log(`Focus zones configured for session ${sessionId}:`, settings.zones); } async processAcceleratedMovement(sessionId, x, y, deltaX, deltaY) { try { const acceleration = this.accelerationSettings.get(sessionId); if (!acceleration || !acceleration.enabled) { return { x, y }; } // Calculate movement speed const speed = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (speed < acceleration.threshold) { return { x, y }; } // Apply acceleration const accelerationFactor = Math.min(acceleration.maxSpeed, 1 + (speed - acceleration.threshold) * acceleration.sensitivity / 10); const acceleratedX = x + deltaX * accelerationFactor; const acceleratedY = y + deltaY * accelerationFactor; return { x: Math.round(acceleratedX), y: Math.round(acceleratedY) }; } catch (error) { throw handleError(error, `Failed to process accelerated movement for session ${sessionId}`); } } async detectPressure(sessionId, x, y, pressure) { try { const pressureSettings = this.pressureSettings.get(sessionId); if (!pressureSettings || !pressureSettings.enabled) { return { normalClick: true, pressureLevel: pressure || 1.0 }; } const actualPressure = pressure || 1.0; const normalizedPressure = Math.min(actualPressure / pressureSettings.maxPressure, 1.0); return { normalClick: normalizedPressure >= pressureSettings.threshold, pressureLevel: normalizedPressure }; } catch (error) { throw handleError(error, `Failed to detect pressure for session ${sessionId}`); } } async trackMultiClick(sessionId, x, y, maxClicks = 10) { try { const now = new Date(); const counter = this.multiClickCounters.get(sessionId) || { count: 0, lastClick: new Date(0), maxClicks }; // Check if this is within the multi-click time window (500ms) const timeSinceLastClick = now.getTime() - counter.lastClick.getTime(); if (timeSinceLastClick < 500 && counter.count < maxClicks) { counter.count++; } else { counter.count = 1; // Reset to single click } counter.lastClick = now; counter.maxClicks = maxClicks; this.multiClickCounters.set(sessionId, counter); return counter.count; } catch (error) { throw handleError(error, `Failed to track multi-click for session ${sessionId}`); } } async filterMouseEvents(sessionId, event) { try { const filter = this.eventFilters.get(sessionId); if (!filter) { return true; // No filter, allow event } const now = new Date(); if (filter.lastEvent) { const timeSinceLastEvent = now.getTime() - filter.lastEvent.getTime(); if (timeSinceLastEvent < filter.debounceMs) { return false; // Filter out this event } } filter.lastEvent = now; return true; // Allow event } catch (error) { throw handleError(error, `Failed to filter mouse event for session ${sessionId}`); } } async setEventFilter(sessionId, debounceMs) { try { const session = this.terminalManager.getSession(sessionId); if (!session) { throw createTerminalError('SESSION_NOT_FOUND', `Session ${sessionId} not found`); } this.eventFilters.set(sessionId, { debounceMs: Math.max(0, Math.min(1000, debounceMs)), // Clamp between 0-1000ms lastEvent: undefined }); } catch (error) { throw handleError(error, `Failed to set event filter for session ${sessionId}`); } } async processFocusFollowMouse(sessionId, x, y) { try { const focusSettings = this.focusSettings.get(sessionId); if (!focusSettings || !focusSettings.enabled) { return null; } // Check which zone the mouse is in for (const zone of focusSettings.zones) { if (x >= zone.x && x < zone.x + zone.width && y >= zone.y && y < zone.y + zone.height) { // Simulate focus change with delay setTimeout(async () => { try { // This would trigger focus change in a real implementation console.log(`Focus would change to session ${zone.sessionId} after ${focusSettings.delay}ms delay`); } catch (error) { console.error('Error in focus follow mouse:', error); } }, focusSettings.delay); return zone.sessionId; } } return null; } catch (error) { throw handleError(error, `Failed to process focus-follow-mouse for session ${sessionId}`); } } getAccelerationSettings(sessionId) { return this.accelerationSettings.get(sessionId) || null; } getPressureSettings(sessionId) { return this.pressureSettings.get(sessionId) || null; } getFocusSettings(sessionId) { return this.focusSettings.get(sessionId) || null; } getEventFilter(sessionId) { const filter = this.eventFilters.get(sessionId); return filter ? { debounceMs: filter.debounceMs } : null; } getMultiClickStatus(sessionId) { const counter = this.multiClickCounters.get(sessionId); return counter ? { count: counter.count, maxClicks: counter.maxClicks } : null; } async cleanup() { this.accelerationSettings.clear(); this.pressureSettings.clear(); this.focusSettings.clear(); this.eventFilters.clear(); this.multiClickCounters.clear(); } }